﻿
//using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using FormCommon;
using System.Drawing;
using HRM.Common.Properties;
//using FormCommon.Properties;
//using NHTC.DataAccess;

public class DataGridHandle
{
    //*******************************************************************
    // Purpose:  Class dùng handle cho Data Grid View
    // Notes:    
    // History:
    //     [Programer]       [DD-MMM-YYYY]       [Action]
    //     LuanNH            10-Aug-2007         Created
    //*******************************************************************  

    private DataGridView withEventsField_m_objGrid;
    private DataGridView m_objGrid
    {
        get { return withEventsField_m_objGrid; }
        set
        {
            if (withEventsField_m_objGrid != null)
            {
                withEventsField_m_objGrid.AllowUserToDeleteRowsChanged -= m_objGrid_AllowUserToDeleteRowsChanged;
                withEventsField_m_objGrid.CellBeginEdit -= m_objGrid_CellBeginEdit;
                withEventsField_m_objGrid.CellEndEdit -= m_objGrid_CellEndEdit;
                withEventsField_m_objGrid.CellMouseClick -= m_objGrid_CellMouseClick;
                withEventsField_m_objGrid.CellStateChanged -= m_objGrid_CellStateChanged;
                withEventsField_m_objGrid.CellValidating -= m_objGrid_CellValidating;
                withEventsField_m_objGrid.ColumnHeaderMouseClick -= m_objGrid_ColumnHeaderMouseClick;
                withEventsField_m_objGrid.DataError -= m_objGrid_DataError;
                withEventsField_m_objGrid.DataSourceChanged -= m_objGrid_DataSourceChanged;
                withEventsField_m_objGrid.KeyDown -= m_objGrid_KeyDown;
                withEventsField_m_objGrid.ColumnAdded -= m_objGrid_ColumnAdded;
                withEventsField_m_objGrid.ColumnRemoved -= m_objGrid_ColumnRemoved;
                withEventsField_m_objGrid.ColumnStateChanged -= m_objGrid_ColumnStateChanged;
                withEventsField_m_objGrid.Paint -= m_objGrid_Paint;
                withEventsField_m_objGrid.CellPainting -= m_objGrid_CellPainting;
                withEventsField_m_objGrid.ColumnWidthChanged -= m_objGrid_ColumnWidthChanged;
                withEventsField_m_objGrid.RowHeaderMouseClick -= m_objGrid_RowHeaderMouseClick;
                withEventsField_m_objGrid.RowsAdded -= m_objGrid_RowsAdded;
                withEventsField_m_objGrid.RowValidated -= m_objGrid_RowValidated;
                withEventsField_m_objGrid.RowValidating -= m_objGrid_RowValidating;
                withEventsField_m_objGrid.UserDeletingRow -= m_objGrid_UserDeletingRow;
                withEventsField_m_objGrid.ReadOnlyChanged -= m_objGrid_ReadOnlyChanged;
                withEventsField_m_objGrid.CellErrorTextChanged -= m_objGrid_CellErrorTextChanged;
                withEventsField_m_objGrid.RowErrorTextChanged -= m_objGrid_RowErrorTextChanged;
                withEventsField_m_objGrid.EditingControlShowing -= m_objGrid_EditingControlShowing;
                withEventsField_m_objGrid.CellValueChanged -= m_objGrid_CellValueChanged;
                withEventsField_m_objGrid.CellDoubleClick -= m_objGrid_CellDoubleClick;
                withEventsField_m_objGrid.SelectionChanged -= m_objGrid_SelectionChanged;
                withEventsField_m_objGrid.CellFormatting -= m_objGrid_CellFormatting;
                withEventsField_m_objGrid.CellContentClick -= m_objGrid_CellContentClick;
                withEventsField_m_objGrid.CellEnter -= m_objGrid_CellEnter;
                withEventsField_m_objGrid.CellMouseLeave -= m_objGrid_CellMouseLeave;
                withEventsField_m_objGrid.PreviewKeyDown -= m_objGrid_PreviewKeyDown;
                withEventsField_m_objGrid.RowEnter -= m_objGrid_RowEnter;
            }
            withEventsField_m_objGrid = value;
            if (withEventsField_m_objGrid != null)
            {
                withEventsField_m_objGrid.AllowUserToDeleteRowsChanged += m_objGrid_AllowUserToDeleteRowsChanged;
                withEventsField_m_objGrid.CellBeginEdit += m_objGrid_CellBeginEdit;
                withEventsField_m_objGrid.CellEndEdit += m_objGrid_CellEndEdit;
                withEventsField_m_objGrid.CellMouseClick += m_objGrid_CellMouseClick;
                withEventsField_m_objGrid.CellStateChanged += m_objGrid_CellStateChanged;
                withEventsField_m_objGrid.CellValidating += m_objGrid_CellValidating;
                withEventsField_m_objGrid.ColumnHeaderMouseClick += m_objGrid_ColumnHeaderMouseClick;
                withEventsField_m_objGrid.DataError += m_objGrid_DataError;
                withEventsField_m_objGrid.DataSourceChanged += m_objGrid_DataSourceChanged;
                withEventsField_m_objGrid.KeyDown += m_objGrid_KeyDown;
                withEventsField_m_objGrid.ColumnAdded += m_objGrid_ColumnAdded;
                withEventsField_m_objGrid.ColumnRemoved += m_objGrid_ColumnRemoved;
                withEventsField_m_objGrid.ColumnStateChanged += m_objGrid_ColumnStateChanged;
                withEventsField_m_objGrid.Paint += m_objGrid_Paint;
                withEventsField_m_objGrid.CellPainting += m_objGrid_CellPainting;
                withEventsField_m_objGrid.ColumnWidthChanged += m_objGrid_ColumnWidthChanged;
                withEventsField_m_objGrid.RowHeaderMouseClick += m_objGrid_RowHeaderMouseClick;
                withEventsField_m_objGrid.RowsAdded += m_objGrid_RowsAdded;
                withEventsField_m_objGrid.RowValidated += m_objGrid_RowValidated;
                withEventsField_m_objGrid.RowValidating += m_objGrid_RowValidating;
                withEventsField_m_objGrid.UserDeletingRow += m_objGrid_UserDeletingRow;
                withEventsField_m_objGrid.ReadOnlyChanged += m_objGrid_ReadOnlyChanged;
                withEventsField_m_objGrid.CellErrorTextChanged += m_objGrid_CellErrorTextChanged;
                withEventsField_m_objGrid.RowErrorTextChanged += m_objGrid_RowErrorTextChanged;
                withEventsField_m_objGrid.EditingControlShowing += m_objGrid_EditingControlShowing;
                withEventsField_m_objGrid.CellValueChanged += m_objGrid_CellValueChanged;
                withEventsField_m_objGrid.CellDoubleClick += m_objGrid_CellDoubleClick;
                withEventsField_m_objGrid.SelectionChanged += m_objGrid_SelectionChanged;
                withEventsField_m_objGrid.CellFormatting += m_objGrid_CellFormatting;
                withEventsField_m_objGrid.CellContentClick += m_objGrid_CellContentClick;
                withEventsField_m_objGrid.CellEnter += m_objGrid_CellEnter;
                withEventsField_m_objGrid.CellMouseLeave += m_objGrid_CellMouseLeave;
                withEventsField_m_objGrid.PreviewKeyDown += m_objGrid_PreviewKeyDown;
                withEventsField_m_objGrid.RowEnter += m_objGrid_RowEnter;
            }
        }
    }
    private TextBox withEventsField_m_txtCellEdit;
    private TextBox m_txtCellEdit
    {
        get { return withEventsField_m_txtCellEdit; }
        set
        {
            if (withEventsField_m_txtCellEdit != null)
            {
                withEventsField_m_txtCellEdit.Leave -= m_txtCellEdit_Leave;
                withEventsField_m_txtCellEdit.KeyDown -= m_txtCellEdit_KeyDown;
            }
            withEventsField_m_txtCellEdit = value;
            if (withEventsField_m_txtCellEdit != null)
            {
                withEventsField_m_txtCellEdit.Leave += m_txtCellEdit_Leave;
                withEventsField_m_txtCellEdit.KeyDown += m_txtCellEdit_KeyDown;
            }
        }
    }
    private ContextMenuStrip mnuContext;
    private FormBase m_Form;
    //=============
    private string _sPrimaryKey;
    //=============
    // store list of merge column
    private List<MergeColumnHeaderInfo> m_dicMergeInfo;
    //=============
    //store list of mapping column for paste data
    private Dictionary<string, string> m_dicMappingColumn;
    //store list of mapping value for paste data
    private Dictionary<string, string> m_dicMappingValue;
    //=============
    // store list of required column
    private List<string> m_dicRequiredColumn;
    // store list of filterable column
    private Dictionary<string, string> m_dicFilterColumn;
    // store list of column format by value
    private Dictionary<string, Dictionary<object, string>> m_dicFormatColumn;
    //=============
    private DataTable m_SourceTable;
    private bool m_bIsLoading;
    private int m_iOrder;
    //=============
    private bool _PasteClipboard;
    private bool _MultiDelete;
    private bool _ConfirmDelete;
    private int _RowSelectCount;
    //=============
    private bool _AutoRequired;
    private bool _AllowFilter;
    private bool _KeepMergeHeader;
    //=============
    private MethodInvoker m_DbClickMethod;
    private MethodInvoker m_SelectMethod;
    //=============
    //Sự kiện kiểm tra dữ liệu paste
    public event Row_PasteVerifyEventHandler Row_PasteVerify;
    public delegate void Row_PasteVerifyEventHandler(DataRow drData, ref bool bPassed);
    //Sự kiện thông báo quá trình paste
    public event Row_PasteProgressEventHandler Row_PasteProgress;
    public delegate void Row_PasteProgressEventHandler(int rowIndex, int rowCount);
    //Sự kiện sau khi cập nhật dữ liệu cho 1 dòng
    public event Row_ValidatedEventHandler Row_Validated;
    public delegate void Row_ValidatedEventHandler(DataGridViewRow row);
    //Sự kiện kiểm tra khi xóa dòng
    public event Row_DeletingEventHandler Row_Deleting;
    public delegate void Row_DeletingEventHandler(DataGridViewRow row, ref bool cancel);
    //Sự kiện trước khi cập nhật dữ liệu cho 1 cell
    public event Cell_ValidatingEventHandler Cell_Validating;
    public delegate void Cell_ValidatingEventHandler(DataGridViewCell cell, ref string inputValue);
    //Sự kiện sau khi cập nhật dữ liệu cho 1 cell
    public event Cell_ValidatedEventHandler Cell_Validated;
    public delegate void Cell_ValidatedEventHandler(DataGridViewCell cell);
    //Sự kiện sau khi cập nhật dữ liệu cho 1 cell
    public event CellButton_ClickEventHandler CellButton_Click;
    public delegate void CellButton_ClickEventHandler(DataGridViewButtonCell cell);
    //Sự kiện format dữ liệu cho 1 cell
    public event Cell_FormatEventHandler Cell_Format;
    public delegate void Cell_FormatEventHandler(string columnName, DataGridViewCellFormattingEventArgs e);
    //Sự kiện sort manual
    public event Column_SortEventHandler Column_Sort;
    public delegate void Column_SortEventHandler(string columnName, SortOrder e);

    private int GetPosX(int iFromColIndex)
    {
        if (m_objGrid.Visible)
        {
            return m_objGrid.GetColumnDisplayRectangle(iFromColIndex, true).X + m_objGrid.Left;
        }
        else
        {
            int iX = m_objGrid.RowHeadersWidth + 2;
            for (int i = iFromColIndex - 1; i >= 0; i += -1)
            {
                if (m_objGrid.Columns[i].Visible)
                {
                    iX += m_objGrid.Columns[i].Width;
                }
            }
            return iX + m_objGrid.Left;
        }
    }

    private int GetPosY(int rowIndex)
    {
        if (m_objGrid.Visible)
        {
            return m_objGrid.GetRowDisplayRectangle(rowIndex, true).Y + m_objGrid.Top;
        }
        else
        {
            int iX = m_objGrid.ColumnHeadersHeight + 2;
            for (int i = rowIndex - 1; i >= 0; i += -1)
            {
                if (m_objGrid.Rows[i].Visible)
                {
                    iX += m_objGrid.Rows[i].Height;
                }
            }
            return iX + m_objGrid.Top;
        }
    }

    private Point GetCellLocation(DataGridViewCell cell)
    {
        Point controlLoc = m_objGrid.FindForm().PointToClient(m_objGrid.Parent.PointToScreen(m_objGrid.Location));
        if (m_objGrid.Visible)
        {
            controlLoc.Y += m_objGrid.GetRowDisplayRectangle(cell.RowIndex, true).Y;
            controlLoc.X += m_objGrid.GetColumnDisplayRectangle(cell.ColumnIndex, true).X;
        }
        else
        {
            int iY = m_objGrid.ColumnHeadersHeight + 2;
            for (int i = cell.RowIndex - 1; i >= 0; i += -1)
            {
                if (m_objGrid.Rows[i].Visible)
                {
                    iY += m_objGrid.Rows[i].Height;
                }
            }
            controlLoc.Y += iY;
            //
            int iX = m_objGrid.RowHeadersWidth + 2;
            for (int i = cell.ColumnIndex - 1; i >= 0; i += -1)
            {
                if (m_objGrid.Columns[i].Visible)
                {
                    iX += m_objGrid.Columns[i].Width;
                }
            }
            controlLoc.X += iX;
        }
        return controlLoc;
    }

    private string GetColumnKey(int columnIndex)
    {
        string retVal = m_objGrid.Columns[columnIndex].DataPropertyName;
        if (string.IsNullOrEmpty(retVal))
        {
            retVal = m_objGrid.Columns[columnIndex].Name;
        }
        return retVal;
    }

    public bool ClipboardPaste
    {
        get
        {
            if (_PasteClipboard)
            {
                _PasteClipboard = false;
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public bool AutoRequired
    {
        get { return _AutoRequired; }
        set
        {
            _AutoRequired = value;
            if (_AutoRequired)
            {
                m_dicRequiredColumn = null;
            }
        }
    }

    public bool AllowFilter
    {
        get { return _AllowFilter; }
        set { _AllowFilter = value; }
    }

    public bool KeepMergeHeader
    {
        get { return _KeepMergeHeader; }
        set
        {
            _KeepMergeHeader = value;
            if (_KeepMergeHeader)
            {
                int iHeight = m_objGrid.ColumnHeadersHeight;
                m_objGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
                m_objGrid.ColumnHeadersHeight = iHeight + 18;
            }
        }
    }

    public bool IsValid
    {
        get
        {
            string sError = CheckError();
            if (!string.IsNullOrEmpty(sError))
                FormGlobals.Message_Information(sError);
            return string.IsNullOrEmpty(sError);
        }
    }

    public string CheckError()
    {
        if (m_SourceTable == null)
            return "";
        foreach (DataRow dr in m_SourceTable.GetErrors())
        {
            if (dr.RowState != DataRowState.Deleted)
            {
                return "Please correct row(s) error before save data";
            }
        }
        return "";
    }

    private string _SortColumn;
    public string SortColumn
    {
        get { return _SortColumn; }
        set { _SortColumn = value; }
    }

    private SortOrder _SortDirection;
    public string SortDirection
    {
        get
        {
            switch (_SortDirection)
            {
                case SortOrder.Ascending:
                    return "ASC";
                case SortOrder.Descending:
                    return "DESC";
                default:
                    return "";
            }
        }
        set
        {
            if (value.ToUpper() == "ASC")
            {
                _SortDirection = SortOrder.Ascending;
            }
            else if (value.ToUpper() == "DESC")
            {
                _SortDirection = SortOrder.Descending;
            }
            else
            {
                _SortDirection = SortOrder.None;
            }
        }
    }

    public DataGridHandle(DataGridView objGrid, string sPrimaryKey = "")
    {
        if (objGrid.FindForm() is FormBase)
        {
            m_Form = (FormBase)objGrid.FindForm();
        }
        //
        _sPrimaryKey = sPrimaryKey;
        m_objGrid = objGrid;
        m_objGrid.RowsDefaultCellStyle.BackColor = FormGlobals.App_GetColor(FormGlobals.CS_GRID_ROW);
        m_objGrid.AlternatingRowsDefaultCellStyle.BackColor = FormGlobals.App_GetColor(FormGlobals.CS_GRID_ROW_ALTER);
        m_objGrid.ShowCellErrors = true;
        m_objGrid.ShowRowErrors = true;
        m_objGrid.RowHeadersWidth = 45;
        m_objGrid.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing;
        m_objGrid.AutoGenerateColumns = false;
        m_objGrid.AllowUserToResizeRows = false;
        //m_objGrid.EnableHeadersVisualStyles = False
        //
        foreach (DataGridViewColumn clm in m_objGrid.Columns)
        {
            if (clm.ReadOnly)
            {
                FormGlobals.Grid_SetColumnViewOnly(clm);
            }
            else
            {
                clm.DefaultCellStyle.SelectionBackColor = Color.LightBlue;
                clm.DefaultCellStyle.SelectionForeColor = Color.Black;
            }
        }
        //
        if (m_objGrid.ColumnHeadersHeight == 18)
        {
            m_objGrid.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
            m_objGrid.ColumnHeadersHeight = 36;
        }
        //merge
        m_dicMergeInfo = new List<MergeColumnHeaderInfo>();
        //
        if (!m_objGrid.ReadOnly)
        {
            m_txtCellEdit = new TextBox();
            m_txtCellEdit.Name = "DataGridHandle_txtCellEdit";
            m_txtCellEdit.TextAlign = HorizontalAlignment.Right;
            m_txtCellEdit.BorderStyle = BorderStyle.FixedSingle;
            m_txtCellEdit.Visible = false;
            m_objGrid.Parent.Controls.Add(m_txtCellEdit);
            //
            if (_AllowFilter)
            {
                mnuContext = new ContextMenuStrip();
                mnuContext.Items.Add(new ToolStripMenuItem("Edit Filter ...", null, new System.EventHandler(ctxMenu_Click), "Filter_Edit"));
                mnuContext.Items.Add(new ToolStripMenuItem("Clear Filter", null, new System.EventHandler(ctxMenu_Click), "Filter_Clear"));
                //
                m_objGrid.TopLeftHeaderCell.ContextMenuStrip = mnuContext;
            }
        }
        //
        FormGlobals.FormControlExt ext = default(FormGlobals.FormControlExt);
        if (!(m_objGrid.Tag is FormGlobals.FormControlExt))
        {
            ext = new FormGlobals.FormControlExt();
            ext.HandleObject = this;
            ext.Tag = m_objGrid.Tag;
        }
        else
        {
            ext = (FormGlobals.FormControlExt)m_objGrid.Tag;
        }
        ext.HandleObject = this;
        m_objGrid.Tag = ext;
    }

    public void SetPrimaryKey(string sColName)
    {
        _sPrimaryKey = sColName;
    }

    public object GetKeyValue()
    {
        if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.IsNewRow)
        {
            return null;
        }
        else
        {
            return Globals .DB_GetValue(((DataRowView)m_objGrid.CurrentRow.DataBoundItem)[_sPrimaryKey], null);
        }
    }

    public T GetKeyValue<T>()
    {
        return Globals.Object_SetValueEx<T>(GetKeyValue());
    }

    public object GetRowValue(string sFieldName)
    {
        if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.IsNewRow)
        {
            return null;
        }
        else
        {
            return Globals.DB_GetValue(((DataRowView)m_objGrid.CurrentRow.DataBoundItem)[sFieldName], null);
        }
    }

    public T GetRowValue<T>(string sFieldName)
    {
        return Globals.Object_SetValueEx<T>(GetRowValue(sFieldName));
    }

    public object GetRowValue(int rowIndex, string sFieldName)
    {
        if (m_objGrid.Rows[rowIndex].DataBoundItem == null)
        {
            return null;
        }
        else
        {
            return Globals.DB_GetValue(((DataRowView)m_objGrid.Rows[rowIndex].DataBoundItem)[sFieldName], null);
        }
    }

    public T GetRowValue<T>(int rowIndex, string sFieldName)
    {
        return Globals.Object_SetValueEx<T>(GetRowValue(rowIndex, sFieldName));
    }

    public DataRow GetRowData()
    {
        if (m_objGrid.CurrentRow == null || m_objGrid.CurrentRow.DataBoundItem == null)
        {
            return null;
        }
        else
        {
            return ((DataRowView)m_objGrid.CurrentRow.DataBoundItem).Row;
        }
    }

    public DataRow GetRowData(int rowIndex)
    {
        if (m_objGrid.RowCount > rowIndex)
        {
            return ((DataRowView)m_objGrid.Rows[rowIndex].DataBoundItem).Row;
        }
        else
        {
            return null;
        }
    }

    public void SetMappingColumn(IDataReader dr, string sDataCol, string sOriginCol)
    {
        m_dicMappingColumn = new Dictionary<string, string>();
        while (dr.Read())
        {
            m_dicMappingColumn.Add(dr[sDataCol].ToString(), dr[sOriginCol].ToString());
        }
        Globals.Reader_Close(dr);
    }

    public Dictionary<string, string> MappingColumn
    {
        get { return m_dicMappingColumn; }
        set { m_dicMappingColumn = value; }
    }

    public object SelectedKeyValue
    {
        get { return GetKeyValue(); }
    }

    public object DataSource
    {
        get { return m_SourceTable; }
        set { m_objGrid.DataSource = value; }
    }

    public void SetMappingValue(IDataReader dr, string sDataCol, string sOriginCol)
    {
        m_dicMappingValue = new Dictionary<string, string>();
        while (dr.Read())
        {
            m_dicMappingValue.Add(dr[sDataCol].ToString(), dr[sOriginCol].ToString());
        }
        Globals.Reader_Close(dr);
    }

    public void Row_DblClick(MethodInvoker DbClickMethod)
    {
        m_DbClickMethod = DbClickMethod;
    }

    public void Row_Selected(MethodInvoker SelectMethod)
    {
        m_SelectMethod = SelectMethod;
    }

    public void Column_MergeHeader(string sMergeHeader, int iFromColIndex, int iColMerge, bool bCover)
    {
        for (int i = 0; i <= iColMerge - 1; i++)
        {
            m_objGrid.Columns[iFromColIndex + i].Resizable = DataGridViewTriState.False;
            if (!bCover)
                m_objGrid.Columns[iFromColIndex + i].HeaderCell.Style.Alignment = DataGridViewContentAlignment.BottomCenter;
        }
        //
        _mergeCalculate = true;
        //
        MergeColumnHeaderInfo objInfo = null;
        foreach (MergeColumnHeaderInfo objInfo_loopVariable in m_dicMergeInfo)
        {
            objInfo = objInfo_loopVariable;
            int colIndex = m_objGrid.Columns[objInfo.FromName].Index;
            if (colIndex <= iFromColIndex && colIndex + objInfo.ColSpan > iFromColIndex)
            {
                objInfo.ColSpan += iColMerge - 1;
                return;
            }
        }
        //
        objInfo = new MergeColumnHeaderInfo();
        objInfo.FromName = m_objGrid.Columns[iFromColIndex].Name;
        objInfo.ColSpan = iColMerge;
        objInfo.Caption = sMergeHeader;
        objInfo.Cover = bCover;
        m_dicMergeInfo.Add(objInfo);
    }

    public void Column_SetVisible(bool visible, params string[] sColumnsName)
    {
        foreach (string sColName in sColumnsName)
        {
            m_objGrid.Columns[sColName].Visible = visible;
        }
    }

    public void Column_MergeHeader(string sFromColName, int iColMerge)
    {
        Column_MergeHeader("", sFromColName, iColMerge, !_KeepMergeHeader);
    }

    //, ByVal bCover As Boolean)
    public void Column_MergeHeader(string sMergeHeader, int iFromColIndex, int iColMerge)
    {
        Column_MergeHeader(sMergeHeader, iFromColIndex, iColMerge, !_KeepMergeHeader);
    }

    public void Column_MergeHeader(string sMergeHeader, string sFromColName, int iColMerge)
    {
        Column_MergeHeader(sMergeHeader, sFromColName, iColMerge, !_KeepMergeHeader);
    }

    public void Column_MergeHeader(string sMergeHeader, string sFromColName, int iColMerge, bool bCover)
    {
        if (!m_objGrid.Columns.Contains(sFromColName))
        {
            throw new Exception(string.Format("MergeColumnHeader: Column name '{0}' not found in data grid column", sFromColName));
        }
        int iIndex = m_objGrid.Columns[sFromColName].Index;
        if (string.IsNullOrEmpty(sMergeHeader))
            sMergeHeader = m_objGrid.Columns[sFromColName].HeaderText;
        Column_MergeHeader(sMergeHeader, iIndex, iColMerge, bCover);
    }

    public void Column_ResetMerge()
    {
        if (m_dicMergeInfo == null)
            return;
        m_dicMergeInfo.Clear();
    }

    public void Filter_AppendColumn(string sColummName, string sCaption)
    {
        if (m_dicFilterColumn == null)
        {
            m_dicFilterColumn = new Dictionary<string, string>();
        }
        if (m_dicFilterColumn.ContainsKey(sColummName))
        {
            m_dicFilterColumn[sColummName] = sCaption;
        }
        else
        {
            m_dicFilterColumn.Add(sColummName, sCaption);
        }
    }

    public void Filter_Reset()
    {
        if ((m_dicFilterColumn != null))
        {
            m_dicFilterColumn.Clear();
        }
    }

    //Public Sub Windows.Forms.FormatColumn(ByVal columnName As String, ByVal dicFormat As Dictionary(Of Object, String))
    //    If m_dicFormatColumn Is Nothing Then m_dicFormatColumn = New Dictionary(Of String, Dictionary(Of Object, String))
    //    columnName = columnName.ToUpper
    //    If m_dicFormatColumn.ContainsKey(columnName) Then m_dicFormatColumn.Remove(columnName)
    //    m_dicFormatColumn.Add(columnName, dicFormat)
    //End Sub

    public void Column_Format(string columnName, object formatValue, string colorBackground)
    {
        Column_Format(columnName, formatValue, colorBackground, null);
    }

    public void Column_Format(string columnName, object formatValue, string colorBackground, string colorText)
    {
        if (m_dicFormatColumn == null)
            m_dicFormatColumn = new Dictionary<string, Dictionary<object, string>>();
        columnName = columnName.ToUpper();
        Dictionary<object, string> dicFormat = default(Dictionary<object, string>);
        if (m_dicFormatColumn.ContainsKey(columnName))
        {
            dicFormat = m_dicFormatColumn[columnName];
        }
        else
        {
            dicFormat = new Dictionary<object, string>();
            m_dicFormatColumn.Add(columnName, null);
        }
        //
        string formatColor = colorBackground;
        if (!string.IsNullOrEmpty(colorText))
            formatColor += ";" + colorText;
        if (dicFormat.ContainsKey(formatValue))
            dicFormat.Remove(formatValue);
        dicFormat.Add(formatValue, formatColor);
        //
        m_dicFormatColumn[columnName] = dicFormat;
    }

    private Dictionary<string, GridLookupInvoker> m_dicLookupInvoker = new Dictionary<string, GridLookupInvoker>();
    private Dictionary<string, string[]> m_dicLookupInit = new Dictionary<string, string[]>();
    private Dictionary<string, Dictionary<string, string>> m_dicLookupMapping = new Dictionary<string, Dictionary<string, string>>();

    private Dictionary<string, object[]> m_dicLookupCache = new Dictionary<string, object[]>();
    private string GetColumnValue(string[] columnInit)
    {
        string columnValue = null;
        if (columnInit.Length > 2 && !string.IsNullOrEmpty(columnInit[2]))
        {
            columnValue = columnInit[2];
        }
        else
        {
            columnValue = columnInit[0];
        }
        return columnValue;
    }

    public void SetLookup(string[] columnInit, GridLookupInvoker lookupMethod)
    {
        SetLookup(columnInit, null, lookupMethod);
    }

    public void SetLookup(string[] columnInit, string columnMapping, GridLookupInvoker lookupMethod)
    {
        string columnCode = null;
        string columnName = null;
        string columnValue = null;
        columnCode = columnInit[0];
        if (columnInit.Length > 1)
        {
            columnName = columnInit[1];
            columnValue = GetColumnValue(columnInit);
        }
        //
        m_dicLookupInit.Add(columnCode, columnInit);
        Dictionary<string, string> dicMapping = new Dictionary<string, string>();
        if (!string.IsNullOrEmpty(columnMapping))
        {
            foreach (string sMapping in columnMapping.Split(new char[]{';'}))
            {
                string[] arrColumn = sMapping.Split(new char[]{'='});
                if (arrColumn.Length > 1)
                {
                    dicMapping.Add(arrColumn[0].Trim().ToUpper(), arrColumn[1].Trim());
                }
            }
        }
        m_dicLookupMapping.Add(columnCode, dicMapping);
        //
        DataGridViewColumn clmCode = m_objGrid.Columns[columnCode];
        DataGridViewColumn clmID = default(DataGridViewColumn);
        if (!string.IsNullOrEmpty(columnValue))
            clmID = m_objGrid.Columns[columnValue];
        //
        if (clmCode == null)
        {
            clmCode = new DataGridViewTextBoxColumn();
            clmCode.Name = columnCode;
            clmCode.DataPropertyName = columnCode;
            clmCode.Width = clmID.Width;
            clmCode.HeaderText = clmID.HeaderText;
            clmCode.HeaderCell = clmID.HeaderCell;
            m_objGrid.Columns.Insert(clmID.Index + 1, clmCode);
        }
        //
        clmCode.DataPropertyName = columnCode;
        clmCode.Visible = true;
        //
        m_dicLookupInvoker.Add(columnCode, lookupMethod);
        //
        if (!(m_objGrid.ReadOnly) && !m_objGrid.Columns.Contains(clmCode.Name + "_clmLookup"))
        {
            DataGridViewButtonColumn clmButton = NewButtonColumn(clmCode.Name + "_clmLookup");
            clmButton.Frozen = clmCode.Frozen;
            //
            m_objGrid.Columns.Insert(clmCode.Index + 1, clmButton);
            //
            Column_MergeHeader(clmCode.HeaderText, clmCode.Index, 2, true);
        }
        //
        if (!string.IsNullOrEmpty(columnValue) && columnValue != columnCode)
        {
            if (!m_objGrid.Columns.Contains(columnValue))
            {
                clmID = new DataGridViewTextBoxColumn();
                clmID.Name = columnValue;
                m_objGrid.Columns.Add(clmID);
            }
            else
            {
                clmID = m_objGrid.Columns[columnValue];
            }
            clmID.DataPropertyName = columnValue;
            clmID.Visible = false;
            clmID.Width = 0;
        }
    }

    private bool _isLookupInvoke;
    private void Cell_Lookup(DataGridViewCell currentCell, LookupMode mode)
    {
        _isLookupInvoke = true;
        try
        {
            string sColumnKey = currentCell.OwningColumn.Name;
            string[] arrInitColumn = m_dicLookupInit[sColumnKey];
            //Get data from cache
            if (mode == LookupMode.CheckData)
            {
                object[] arrObject = LookupCacheGet(arrInitColumn, currentCell.OwningRow);
                if ((arrObject != null))
                {
                    for (int i = 0; i <= arrInitColumn.Length - 1; i++)
                    {
                        if (currentCell.DataGridView.Columns.Contains(arrInitColumn[i]))
                        {
                            currentCell.OwningRow.Cells[arrInitColumn[i]].Value = arrObject[i];
                        }
                    }
                    return;
                }
            }
            //Pre invoke
            switch (mode)
            {
                case LookupMode.SelectData:
                    m_objGrid.CurrentCell = currentCell;
                    m_objGrid.BeginEdit(false);
                    break;
            }
            //Invoke method
            string columnValue = GetColumnValue(arrInitColumn);
            DataRowEx drEx = m_dicLookupInvoker[sColumnKey].Invoke(currentCell, columnValue, mode);
            //Update data
            if (((drEx != null) && drEx.HasData))
            {
                string rowKey = null;
                DataRowView objRowData = (DataRowView)currentCell.OwningRow.DataBoundItem;
                Dictionary<string, string> dicMapping = m_dicLookupMapping[sColumnKey];
                foreach (string dataKey in arrInitColumn)
                {
                    if (string.IsNullOrEmpty(dataKey))
                        continue;
                    if ((dicMapping != null) && dicMapping.ContainsKey(dataKey.ToUpper()))
                    {
                        rowKey = dicMapping[dataKey.ToUpper()];
                    }
                    else
                    {
                        rowKey = dataKey;
                    }
                    if (m_objGrid.Columns.Contains(dataKey))
                    {
                        if (m_objGrid.Columns[dataKey] is DataGridViewCheckBoxColumn)
                        {
                            if ( Globals.Object_GetValue<bool>(drEx.Item(rowKey)))
                            {
                                currentCell.OwningRow.Cells[rowKey].Value = "1";
                            }
                            else
                            {
                                currentCell.OwningRow.Cells[rowKey].Value = "0";
                            }
                        }
                        else
                        {
                            currentCell.OwningRow.Cells[dataKey].Value = Globals.DB_GetNull(drEx.Item(rowKey));
                        }
                    }
                    else
                    {
                        if (objRowData == null)
                            continue;
                        if ((m_SourceTable != null) && m_SourceTable.Columns.Contains(rowKey))
                        {
                            if (drEx.Item(rowKey) is bool)
                            {
                                if ( Globals.Object_GetValue<bool>(drEx.Item(rowKey)))
                                {
                                    objRowData[rowKey] = "1";
                                }
                                else
                                {
                                    objRowData[rowKey] = "0";
                                }
                            }
                            else
                            {
                                objRowData[dataKey] = Globals.DB_GetNull(drEx.Item(rowKey));
                            }
                        }
                    }
                }
                //
                currentCell.ErrorText = "";
                //Clear current error check
                //
                if (Cell_Validated != null)
                {
                    Cell_Validated(currentCell);
                }
            }
            //Post invoke
            switch (mode)
            {
                case LookupMode.SelectData:
                    m_objGrid.EditingControl.Text = Globals.DB_GetString(currentCell.Value);
                    m_objGrid.EndEdit();
                    break;
                case LookupMode.CheckData:
                case LookupMode.GetData:
                    break;
                //Do nothing
            }
            //Store value array to cache
            if (!Globals.IsDBNull(currentCell.OwningRow.Cells[columnValue].Value) && ((drEx != null) && drEx.HasData))
            {
                LookupCacheSet(arrInitColumn, currentCell.OwningRow);
            }
        }
        catch (Exception ex)
        {
            //Throw ex
            FormGlobals.Message_Error(ex);
        }
        finally
        {
            _isLookupInvoke = false;
        }
    }

    private void LookupCacheSet(string[] arrInit, DataGridViewRow row)
    {
        if (Null.IsNull(row.Cells[arrInit[0]].Value))
            return;
        string sCacheKey = arrInit[0] + Globals.vbTab + row.Cells[arrInit[0]].Value;
        if (!m_dicLookupCache.ContainsKey(sCacheKey))
        {
            object[] arrObject = new object[arrInit.Length];
            for (int i = 0; i <= arrInit.Length - 1; i++)
            {
                if (row.DataGridView.Columns.Contains(arrInit[i]))
                {
                    arrObject[i] = row.Cells[arrInit[i]].Value;
                }
            }
            m_dicLookupCache.Add(sCacheKey, arrObject);
        }
    }

    private object[] LookupCacheGet(string[] arrInit, DataGridViewRow row)
    {
        string sCacheKey = arrInit[0] + Globals.vbTab + row.Cells[arrInit[0]].Value;
        if (m_dicLookupCache.ContainsKey(sCacheKey))
        {
            return m_dicLookupCache[sCacheKey];
        }
        else
        {
            return null;
        }
    }

    private Dictionary<string, CharacterCasing> m_dicCaseInput = new Dictionary<string, CharacterCasing>();
    public void SetCaseInput(string columnName, CharacterCasing caseInput)
    {
        if (!m_objGrid.Columns.Contains(columnName))
        {
            throw new ArgumentException("Column name '" + columnName + "' not found", "columnName");
        }
        m_dicCaseInput.Add(columnName, caseInput);
    }

    public void SetCaseInput(CharacterCasing caseInput, params string[] columnArray)
    {
        foreach (string columnName in columnArray)
        {
            SetCaseInput(columnName, caseInput);
        }
    }

    private Dictionary<string, int> m_dicMaxInput = new Dictionary<string, int>();
    public void SetMaxInput(string columnName, int maxInput)
    {
        if (!m_objGrid.Columns.Contains(columnName))
        {
            throw new ArgumentException("Column name '" + columnName + "' not found", "columnName");
        }
        if (m_SourceTable != null && m_SourceTable.Columns.Contains(columnName))
        {
            m_SourceTable.Columns[columnName].MaxLength = maxInput;
        }
        m_dicMaxInput.Add(columnName, maxInput);
    }

    public void SetMaxInput(int maxInput, params string[] columnArray)
    {
        foreach (string columnName in columnArray)
        {
            SetMaxInput(columnName, maxInput);
        }
    }

    //======================
    // Handle events
    //======================
    private const string cs_clmDeleteRow = "clmDeleteRow";
    private void m_objGrid_AllowUserToDeleteRowsChanged(object sender, System.EventArgs e)
    {
        InitDeleteColumn();
    }

    private void InitDeleteColumn()
    {
        DataGridViewButtonColumn clmDeleteRow = default(DataGridViewButtonColumn);
        if (m_objGrid.Columns.Contains(cs_clmDeleteRow))
            clmDeleteRow = (DataGridViewButtonColumn)m_objGrid.Columns[cs_clmDeleteRow];
        if (m_objGrid.AllowUserToDeleteRows)
        {
            if (clmDeleteRow == null)
            {
                clmDeleteRow = NewButtonColumn(cs_clmDeleteRow);
                m_objGrid.Columns.Add(clmDeleteRow);
            }
        }
        else
        {
            if (clmDeleteRow != null)
                clmDeleteRow.Visible = false;
        }
    }

    private DataGridViewButtonColumn NewButtonColumn(string clmName)
    {
        DataGridViewButtonColumn clmButton = new DataGridViewButtonColumn();
        clmButton.Name = clmName;
        clmButton.Width = 22;
        clmButton.HeaderText = "";
        clmButton.DefaultCellStyle.SelectionBackColor = SystemColors.Control;
        clmButton.DefaultCellStyle.BackColor = SystemColors.Control;
        return clmButton;
    }

    private void m_objGrid_CellBeginEdit(object sender, System.Windows.Forms.DataGridViewCellCancelEventArgs e)
    {
        try
        {
            m_objGrid.CurrentCell.Tag = "Edit";
            m_objGrid.CurrentRow.Tag = "Edit";
            //
            if ((m_Form != null))
                m_Form.HandleEsc = true;
        }
        catch
        {
        }
    }

    private void m_objGrid_CellEndEdit(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {
            m_objGrid.CurrentCell.Tag = null;
            if (m_objGrid.Rows[e.RowIndex].Tag == null && (m_Form != null))
                m_Form.HandleEsc = false;
        }
        catch
        {
        }
    }

    private void m_objGrid_CellMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
    {
        try
        {
            if (m_objGrid.ReadOnly == false && e.Button == MouseButtons.Right && e.RowIndex >= 0 && e.ColumnIndex >= 0)
            {
                if ((m_objGrid.Columns[e.ColumnIndex].ValueType != null) && "Int32,Int64,Int16,Decimal,String".Contains(m_objGrid.Columns[e.ColumnIndex].ValueType.Name))
                {
                    if (m_objGrid.SelectedRows.Count > 0)
                        return;

                    DataGridViewCell mCell = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];

                    if (!mCell.Selected || mCell.ReadOnly || !(mCell is DataGridViewTextBoxCell || (mCell is DataGridViewComboBoxCell & m_objGrid.Columns[e.ColumnIndex].ValueType.Name == "String")))
                        return;

                    Rectangle r = m_objGrid.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
                    m_txtCellEdit.Parent = m_objGrid.Parent;
                    m_txtCellEdit.Location = new Point(r.X + m_objGrid.Left, r.Y + +m_objGrid.Top);
                    m_txtCellEdit.Size = new Size(r.Width, r.Height);
                    m_txtCellEdit.Text = Globals.Object_GetDisplayValue(mCell.Value);
                    if (mCell.OwningColumn is DataGridViewTextBoxColumn)
                    {
                        m_txtCellEdit.MaxLength = ((DataGridViewTextBoxColumn)mCell.OwningColumn).MaxInputLength;
                    }
                    else
                    {
                        m_txtCellEdit.MaxLength = 0;
                    }
                    m_txtCellEdit.Tag = m_objGrid.Columns[e.ColumnIndex].ValueType.Name;
                    if (m_txtCellEdit.Tag == "String")
                    {
                        m_txtCellEdit.TextAlign = HorizontalAlignment.Left;
                    }
                    else
                    {
                        m_txtCellEdit.TextAlign = HorizontalAlignment.Right;
                    }
                    m_txtCellEdit.Visible = true;
                    m_txtCellEdit.BringToFront();
                    //
                    m_txtCellEdit.Focus();
                    m_txtCellEdit.SelectAll();
                    m_objGrid.Enabled = false;
                    //m_objGrid.Tag = Nothing
                    if ((m_Form != null))
                        m_Form.HandleEsc = true;
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex, "DataGridHandle.CellMouseClick");
        }
    }

    private void m_objGrid_CellStateChanged(object sender, System.Windows.Forms.DataGridViewCellStateChangedEventArgs e)
    {
        try
        {
            if (m_bIsLoading || m_objGrid.SelectionMode != DataGridViewSelectionMode.CellSelect)
                return;

            if (e.StateChanged == DataGridViewElementStates.Selected && e.Cell.Selected)
            {
                //If m_objGrid.GetCellCount(DataGridViewElementStates.Selected) <= 1 Then Return
                if (m_objGrid.SelectedCells.Count <= 1)
                    return;
                //If m_objGrid.GetCellCount(DataGridViewElementStates.Selected) = 2 Then
                if (m_objGrid.SelectedCells.Count == 2)
                {
                    if (m_objGrid.SelectedCells[0].ColumnIndex != m_objGrid.SelectedCells[1].ColumnIndex)
                    {
                        e.Cell.Selected = false;
                    }
                    return;
                }
                if (m_objGrid.SelectedCells[2].ColumnIndex == m_objGrid.SelectedCells[1].ColumnIndex)
                {
                    if (e.Cell.ColumnIndex != m_objGrid.SelectedCells[2].ColumnIndex)
                    {
                        e.Cell.Selected = false;
                    }
                }
                else
                {
                    e.Cell.Selected = false;
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex, "DataGridHandle.CellStateChanged");
        }
    }

    //Sự kiện kiểm tra hợp lệ của Cell - quan trọng
    private void m_objGrid_CellValidating(object sender, System.Windows.Forms.DataGridViewCellValidatingEventArgs e)
    {
        try
        {
            if (m_objGrid.EditingControl == null || m_objGrid.CurrentCell.Tag == null)
                return;
            if (m_objGrid.Columns[e.ColumnIndex] is DataGridViewTextBoxColumn)
            {
                //
                string inputValue = m_objGrid.EditingControl.Text;
                if (Cell_Validating != null)
                {
                    Cell_Validating(m_objGrid.CurrentCell,ref inputValue);
                }
                m_objGrid.EditingControl.Text = inputValue;
                //
                switch (m_objGrid.CurrentCell.ValueType.Name)
                {
                    case "DateTime":
                        string sValue = m_objGrid.EditingControl.Text;
                        if ((m_objGrid.CurrentCell.OwningColumn.HeaderCell.Tag != null) && m_objGrid.CurrentCell.OwningColumn.HeaderCell.Tag.ToString().ToUpper() == "DATESHORT")
                        {
                            m_objGrid.CancelEdit();
                            ValidateDateShort(sValue, e.ColumnIndex, e.RowIndex);
                            return;
                        }
                        System.DateTime mDate = default(System.DateTime);
                        bool isValid = false;

                        if (m_objGrid.CurrentCell.OwningColumn.DefaultCellStyle.Format
                        
                            == Globals.CS_DISPLAY_DATE_FORMAT)
                        {
                                Globals.Date_FixString(ref sValue);
                                isValid = Globals.Date_TryParseEx(sValue,ref mDate);
                                if (isValid)
                                {
                                    m_objGrid.EditingControl.Text = mDate.ToString(Globals.CS_EDIT_DATE_FORMAT);
                                    m_objGrid.CurrentCell.Value = mDate;
                                    return;
                                }
                        }
                        else if (m_objGrid.CurrentCell.OwningColumn.DefaultCellStyle.Format
                            == Globals.CS_DISPLAY_TIME_FORMAT){
                                if (Globals.IsDBNull(m_objGrid.CurrentCell.Value))
                                {
                                    mDate = DateTime.Today;
                                }
                                else
                                {
                                    mDate = (DateTime)m_objGrid.CurrentCell.Value;
                                }
                                mDate = Globals.Time_ParseEx(mDate, sValue);
                                isValid = !Null.IsNull(mDate);
                                if (isValid)
                                {
                                    m_objGrid.EditingControl.Text = mDate.ToString(Globals.CS_EDIT_TIME_FORMAT);
                                    m_objGrid.CurrentCell.Value = mDate;
                                    return;
                                }
                                break;
                        }

                        if (!isValid)
                        {
                            m_objGrid.EditingControl.Text = "";
                            m_objGrid.CurrentCell.Value = DBNull.Value;
                        }
                        break;
                    case "Double":
                    case "Decimal":
                    case "Integer":
                    case "Long":
                    case "Int32":
                    case "Int64":
                    case "Int16":
                        string sText = m_objGrid.EditingControl.Text;
                        if (sText.Length > 1)
                        {
                            string sFormula = null;
                            decimal iValue = default(decimal);
                            if (sText.StartsWith("="))
                            {
                                sFormula = sText.Substring(1);
                                if (Globals.CS_DECIMAL_SYMBOL != ".")
                                    sFormula = sFormula.Replace(Globals.CS_DECIMAL_SYMBOL, ".");
                                if ("+-".Contains(sFormula[0].ToString()))
                                {
                                    Formula_Prepare(sFormula, ref sFormula, ref iValue);
                                    if (!string.IsNullOrEmpty(sFormula))
                                    {
                                        Formula_Calculate(m_objGrid.CurrentCell, sFormula, iValue);
                                        m_objGrid.EditingControl.Text = Globals.Object_GetValue<string>(m_objGrid.CurrentCell.Value);
                                        sFormula = "";
                                    }
                                    else
                                    {
                                        sFormula = sText.Substring(1);
                                    }
                                }
                                if (!string.IsNullOrEmpty(sFormula))
                                {
                                    iValue = Formula_Evaluate(sFormula);
                                    if (Null.IsNull(iValue))
                                    {
                                        m_objGrid.CancelEdit();
                                    }
                                    else
                                    {
                                        m_objGrid.CurrentCell.Value = iValue;
                                        m_objGrid.EditingControl.Text = Globals.Object_GetValue<string>(m_objGrid.CurrentCell.Value);
                                    }
                                }
                            }
                            else
                            {
                                if (!"+-".Contains(sText[0].ToString()) && Formula_Prepare(sText, ref sFormula, ref iValue))
                                {
                                    if (!string.IsNullOrEmpty(sFormula))
                                    {
                                        Formula_Calculate(m_objGrid.CurrentCell, sFormula, iValue);
                                        m_objGrid.EditingControl.Text = Globals.Object_GetValue<string>(m_objGrid.CurrentCell.Value);
                                    }
                                }
                            }
                        }
                        if (m_objGrid.CurrentCell.OwningColumn.HeaderCell.Tag == "DateShort")
                        {
                            sText = m_objGrid.EditingControl.Text;
                            m_objGrid.CancelEdit();
                            ValidateDateShort(sText, e.ColumnIndex, e.RowIndex);
                        }
                        return;
                    default:
                        return;
                }
                //Clear error data
                DataTable mDataSource = (DataTable)m_objGrid.DataSource;
                string sColumnName = m_objGrid.Columns[e.ColumnIndex].DataPropertyName;
                if (mDataSource.Columns.Contains(sColumnName) && (mDataSource.Columns[sColumnName].DefaultValue != null))
                {
                    m_objGrid.CurrentCell.Value = mDataSource.Columns[sColumnName].DefaultValue;
                    m_objGrid.EditingControl.Text = Globals.DB_GetString(mDataSource.Columns[sColumnName].DefaultValue);
                }
                else
                {
                    m_objGrid.CurrentCell.Value = DBNull.Value;
                    m_objGrid.EditingControl.Text = "";
                }

            }
            else if (m_objGrid.Columns[e.ColumnIndex] is DataGridViewComboBoxColumn)
            {
                ComboBox objComboBox = (ComboBox)m_objGrid.EditingControl;
                long iIndex = objComboBox.FindString( Globals.Object_GetValue<string>(e.FormattedValue));
                if (iIndex >= 0)
                {
                    //objComboBox.SelectedIndex = -1
                    objComboBox.SelectedIndex = (int)iIndex;
                }
                else
                {
                    if (objComboBox.Items.Count > 0)
                    {
                        objComboBox.SelectedIndex = 0;
                    }
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex, "DataGridHandle.CellValidating");
        }
    }

    private void ValidateDateShort(string sValue, int iColumnIndex, int iRowIndex)
    {
        string sColName = m_objGrid.Columns[iColumnIndex].Name;
        System.DateTime mDate = default(System.DateTime);
        if (!sColName.Contains("_YEAR"))
        {
            bool bShortDate = false;
            bShortDate = (sValue.Length < 6 || sValue.Contains("##-???"));
            Globals.Date_FixString(ref sValue);

            if (Globals.IsDate(ref sValue))
            {
                
                //Globals.Object_SetValue(sValue, ref mDate);

                mDate = Globals.Object_SetValueEx<DateTime>(sValue);

                sValue = mDate.ToString("dd-MMM");
                sColName = m_objGrid.Columns[iColumnIndex].Name + "_YEAR";

                if (!bShortDate)
                {
                    if (m_objGrid.Columns.Contains(sColName))
                    {
                        m_objGrid.Rows[iRowIndex].Cells[sColName].Value = mDate.Year;
                    }
                }
                else
                {
                    if (m_objGrid.EditingControl.Text == "29-FEB")
                    {
                        //
                    }
                    else
                    {
                        string sYear = null;
                        if (m_objGrid.Columns.Contains(sColName))
                        {
                            sYear = Globals.DB_GetString(m_objGrid.Rows[iRowIndex].Cells[sColName].Value);
                        }
                        if (string.IsNullOrEmpty(sYear))
                            sYear = "1999";
                        //"1999"

                        //Globals.Object_SetValue(sValue + "-" + sYear, ref mDate);

                        mDate = Globals.Object_SetValueEx<DateTime>(sValue + "-" + sYear);
                    }
                }
                m_objGrid.Rows[iRowIndex].Cells[iColumnIndex].Value = mDate;
                return;
            }
            else
            {
                m_objGrid.Rows[iRowIndex].Cells[iColumnIndex].Value = DBNull.Value;
            }
        }
        else
        {
            //Verify
            sColName = sColName.Replace("_YEAR", "");
            string sText = m_objGrid.Rows[iRowIndex].Cells[sColName].EditedFormattedValue.ToString();

            //Globals.Object_SetValue((sText + "-" + sValue), ref mDate);

            mDate = Globals.Object_SetValueEx<DateTime>(sText + "-" + sValue);

            if (Null.IsNull(mDate))
            {
                //m_objGrid.EditingControl.Text = ""
                m_objGrid.Rows[iRowIndex].Cells[sColName].Value = DBNull.Value;


                //Globals.Object_SetValue(sText + "-" + "1999", ref mDate);
                mDate = Globals.Object_SetValueEx<DateTime>(sText + "-" + "1999");
            }
            else
            {
                m_objGrid.Rows[iRowIndex].Cells[iColumnIndex].Value = mDate.Year;
            }
            m_objGrid.Rows[iRowIndex].Cells[sColName].Value = mDate;
        }
    }

    private void m_objGrid_ColumnHeaderMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
    {
        try
        {
            m_bIsLoading = true;
            //Support full column select
            if (m_objGrid.Columns[e.ColumnIndex].SortMode == DataGridViewColumnSortMode.NotSortable)
            {
                m_objGrid.ClearSelection();
                foreach (DataGridViewRow mRow in m_objGrid.Rows)
                {
                    if (!mRow.IsNewRow)
                    {
                        mRow.Cells[e.ColumnIndex].Selected = true;
                    }
                }
                return;
            }
            //Support Programmatic sort
            if (m_objGrid.Columns[e.ColumnIndex].SortMode == DataGridViewColumnSortMode.Programmatic)
            {
                if (_SortColumn != m_objGrid.Columns[e.ColumnIndex].Name)
                {
                    if (!string.IsNullOrEmpty(_SortColumn) && m_objGrid.Columns.Contains(_SortColumn))
                        m_objGrid.Columns[_SortColumn].HeaderCell.SortGlyphDirection = SortOrder.None;
                    _SortColumn = m_objGrid.Columns[e.ColumnIndex].Name;
                    _SortDirection = SortOrder.Ascending;
                }
                else
                {
                    if (_SortDirection == SortOrder.None)
                    {
                        _SortDirection = SortOrder.Ascending;
                    }
                    else
                    {
                        _SortDirection = 3 - _SortDirection;
                    }
                }
                //
                if (Column_Sort != null)
                {
                    Column_Sort(_SortColumn, _SortDirection);
                }
                //
                m_objGrid.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = _SortDirection;
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
        finally
        {
            m_bIsLoading = false;
        }
    }

    private void m_objGrid_DataError(object sender, System.Windows.Forms.DataGridViewDataErrorEventArgs e)
    {
        try
        {
            string sMsg = null;
            if ((e.Exception != null))
            {
                sMsg = e.Exception.Message;
            }
            else
            {
                return;
            }
            if ((e.Context & DataGridViewDataErrorContexts.Commit) != 0)
            //if ((e.Context & DataGridViewDataErrorContexts.Parsing) != 0)
            {
                if ((m_objGrid.CurrentRow != null) && !m_objGrid.CurrentRow.IsNewRow)
                {
                    if (System.Text.RegularExpressions.Regex.IsMatch(sMsg, "Column '.*' does not allow nulls."))
                    {
                        int iPos = sMsg.IndexOf("'") + 1;
                        string sColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
                        if (!m_objGrid.Columns.Contains(sColumn))
                        {
                            e.ThrowException = true;
                            return;
                        }

                        int iColIndex = m_objGrid.Columns[sColumn].Index;
                        if (m_objGrid.CurrentRow.Cells[iColIndex].Visible)
                        {
                            m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells[iColIndex];
                        }
                        string sColHeader = m_objGrid.Columns[iColIndex].HeaderText;
                        if (string.IsNullOrEmpty(sColHeader))
                        {
                            sColHeader = m_objGrid.Columns[iColIndex].ToolTipText;
                        }
                        sMsg = System.Text.RegularExpressions.Regex.Replace(sMsg, "(Column ').*(' does not allow nulls.)", "$1 " + sColHeader + " $2");
                        //m_objGrid.CurrentRow.Cells(iColIndex).ErrorText = sMsg
                    }
                    else if (sMsg == "Input string was not in a correct format.")
                    {
                        string sDataType = null;
                        switch (m_objGrid.Columns[e.ColumnIndex].ValueType.Name)
                        {
                            case "Int16":
                            case "Int32":
                            case "Int64":
                            case "Integer":
                            case "Long":
                            case "Short":
                                sDataType = "Integer";
                                break;
                            case "Float":
                            case "Double":
                            case "Single":
                                sDataType = "Numeric";
                                break;
                            case "Date":
                            case "DateTime":
                                sDataType = "Date";
                                break;
                            default:
                                sDataType = m_objGrid.Columns[e.ColumnIndex].ValueType.Name;
                                break;
                        }
                        sMsg = "Please input " + sDataType + " data type";
                        //m_objGrid.CurrentCell.ErrorText = sMsg
                    }
                    else if (sMsg.IndexOf("The string was not recognized as a valid DateTime") > -1)
                    {
                        sMsg = "Please input Date data type";
                        //m_objGrid.CurrentCell.ErrorText = sMsg
                    }
                    else if (sMsg.Contains("unique"))
                    {
                        int iPos = sMsg.IndexOf("'") + 1;
                        string arrColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
                        string sColHeader = "";
                        foreach (string sColumn2 in arrColumn.Split(new char[]{','}))
                        {
                            string sColumn = sColumn2;
                            if (m_objGrid.Columns.Contains(Globals.String_Capitalize(sColumn)))
                                sColumn = Globals.String_Capitalize(sColumn);
                            if (!m_objGrid.Columns.Contains(sColumn))
                                return;

                            int iColIndex = m_objGrid.Columns[sColumn].Index;
                            if (m_objGrid.CurrentRow.Cells[iColIndex].Visible)
                            {
                                m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells[iColIndex];
                            }
                            string tmpHeader = m_objGrid.Columns[iColIndex].HeaderText;
                            if (string.IsNullOrEmpty(tmpHeader))
                            {
                                tmpHeader = m_objGrid.Columns[iColIndex].ToolTipText;
                            }
                            if (sColHeader.Length > 0)
                                sColHeader += ", ";
                            sColHeader += tmpHeader;
                        }
                        sMsg = "Please input unique value for column '" + sColHeader + "'";
                    }
                    else if (sMsg == "DataGridViewComboBoxCell value is not valid.")
                    {
                        sMsg = "Cell value not contains in list";
                    }
                    else if (sMsg.IndexOf("MaxLength limit") > -1)
                    {
                        int iPos = sMsg.IndexOf("'") + 1;
                        string sColumn = sMsg.Substring(iPos, sMsg.IndexOf("'", iPos + 1) - iPos);
                        if (!m_objGrid.Columns.Contains(sColumn))
                        {
                            e.ThrowException = true;
                            return;
                        }
                        int iColIndex = m_objGrid.Columns[sColumn].Index;
                        if (m_objGrid.CurrentRow.Cells[iColIndex].Visible)
                        {
                            m_objGrid.CurrentCell = m_objGrid.CurrentRow.Cells[iColIndex];
                        }
                        string sColHeader = m_objGrid.Columns[iColIndex].HeaderText;
                        if (string.IsNullOrEmpty(sColHeader))
                        {
                            sColHeader = m_objGrid.Columns[iColIndex].ToolTipText;
                        }
                        sMsg = "Cannot set column '" + sColHeader + "', the max length input of this column is " + m_SourceTable.Columns[sColumn].MaxLength.ToString() + " character(s)";
                    }
                    FormGlobals.Message_Information(sMsg);
                    e.Cancel = true;
                    if ((m_Form != null))
                        m_Form.HandleEsc = true;
                }
                e.ThrowException = false;
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private bool m_IsLoading;
    private void m_objGrid_DataSourceChanged(object sender, System.EventArgs e)
	{
		m_IsLoading = true;
		try {
			_prevRowIndex = -1;
			if (m_objGrid.DataSource is DataTable) {
				m_SourceTable = (DataTable)m_objGrid.DataSource;
			} else if (m_objGrid.DataSource is DataView) {
				m_SourceTable = ((DataView)m_objGrid.DataSource).Table;
			} else {
				m_SourceTable = null;
				return;
			}
			//Set key
			if (!string.IsNullOrEmpty(_sPrimaryKey)) {
                Globals.DataTable_SetKey(m_SourceTable, _sPrimaryKey);
			}
			//Required
			if (_AutoRequired) {
				foreach (DataColumn mColumn in m_SourceTable.Columns) {
					if (mColumn.AllowDBNull == false && !m_objGrid.Columns.Contains(mColumn.ColumnName)) {
						mColumn.AllowDBNull = true;
					}
				}
			} else {
				//required
				if (m_dicRequiredColumn == null) {
					m_dicRequiredColumn = new List<string>();

					foreach (DataColumn mColumn in m_SourceTable.Columns) {
						if (mColumn.AllowDBNull == false) {
							m_dicRequiredColumn.Add(mColumn.ColumnName.ToUpper());
							mColumn.AllowDBNull = true;
						}
					}
				}
			}
			//
			//For i As Integer = 0 To m_objGrid.Columns.Count - 1
			int i = -1;
			while (i < m_objGrid.Columns.Count - 1) {
				i += 1;
				DataGridViewColumn clm = m_objGrid.Columns[i];
				if ((clm.DataPropertyName != null) && m_SourceTable.Columns.Contains(clm.DataPropertyName)) {
					//(text column only)
					if (clm is DataGridViewTextBoxColumn) {
						if (Globals.Type_IsNumeric(m_SourceTable.Columns[clm.DataPropertyName].DataType) && clm is DataGridViewTextBoxColumn) {
							//Auto align number column 
							clm.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
							continue;
						} else if (m_SourceTable.Columns[clm.DataPropertyName].DataType.Name == "DateTime") {
							if (!m_objGrid.ReadOnly && !clm.ReadOnly && clm.DefaultCellStyle.Format == Globals.CS_DISPLAY_DATE_FORMAT) {
								//Auto insert button column calendar
								if (!m_objGrid.Columns.Contains(clm.Name + "_clmCalendar")) {
									DataGridViewButtonColumn clmButton = NewButtonColumn(clm.Name + "_clmCalendar");
									m_objGrid.Columns.Insert(clm.Index + 1, clmButton);
									//
									Column_MergeHeader(clm.HeaderText, clm.Index, 2, true);
									//
									i += 1;
								}
							} else {
								//Auto align date column 
								clm.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
							}
						}
					}
				}
			}
			//Next
			//Init lookup
			if (m_dicLookupInvoker.Count > 0) {
				foreach (string sKey in m_dicLookupInvoker.Keys) {
					string[] arrInit = m_dicLookupInit[sKey];
					bool bIsOK = true;
					foreach (string colName_loopVariable in arrInit) {
						string colName = colName_loopVariable;
						if (!string.IsNullOrEmpty(colName) && !m_SourceTable.Columns.Contains(colName)) {
							m_SourceTable.Columns.Add(colName);
							bIsOK = false;
						}
					}
					//
					foreach (DataGridViewRow row in m_objGrid.Rows) {
						if (row.IsNewRow)
							continue;
						if (!bIsOK) {
							Cell_Lookup(row.Cells[sKey], LookupMode.GetData);
						} else {
							LookupCacheSet(arrInit, row);
						}
					}
				}
			}
			//Init Delete column
			InitDeleteColumn();
			//Pre select for prevent slow on first selection 
			int x = 0;
			foreach (DataGridViewRow mRow in m_objGrid.Rows) {
				if (mRow.Cells[0].Visible)
					mRow.Cells[0].Selected = true;
				x = +1;
				if (x > 5)
					break; // TODO: might not be correct. Was : Exit For
			}
			m_objGrid.ClearSelection();
			//First row select
			if (m_objGrid.Rows.Count > 0) {
				foreach (DataGridViewCell cell in m_objGrid.Rows[0].Cells) {
					if (cell.Visible) {
						m_objGrid.CurrentCell = cell;
						if (m_objGrid.Visible && m_objGrid.CurrentRow.Index != _prevRowIndex && (m_SelectMethod != null)) {
							_prevRowIndex = m_objGrid.CurrentRow.Index;
							m_SelectMethod.Invoke();
						}
						break; // TODO: might not be correct. Was : Exit For
					}
				}
			}
			//Column Sort
			if (!string.IsNullOrEmpty(_SortColumn) && m_objGrid.Columns.Contains(_SortColumn)) {
				m_objGrid.Columns[_SortColumn].HeaderCell.SortGlyphDirection = _SortDirection;
			}
		} catch (Exception ex) {
			FormGlobals.Message_Error(ex, "DataGridHandle.DataSourceChange");
		} finally {
			m_IsLoading = false;
		}
	}

    private void m_objGrid_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
    {
        try
        {
            if ((e.Control == true & e.KeyCode == Keys.C) | (e.Control == true & e.KeyCode == Keys.Insert))
            {
                if (m_objGrid.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable)
                {
                    m_objGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.Disable;
                }
                //Copy here
                //CopyToClipboard()
                FormGlobals.App_ProgressShow(CopyToClipboard, "Copy data to clipboard");
                e.Handled = true;
            }
            else if ((e.Control == true & e.KeyCode == Keys.V) | (e.Shift == true & e.KeyCode == Keys.Insert))
            {
                //Paste here
                if (m_objGrid.ReadOnly)
                    return;
                FormGlobals.App_ProgressShow(PasteFromClipboard, "Paste data from clipboard");
                e.Handled = true;
            }
            else if (e.KeyCode == Keys.Delete)
            {
                if (m_objGrid.ReadOnly || m_objGrid.SelectedRows.Count > 0)
                    return;
                //Support delete multiple cells
                foreach (DataGridViewCell mCell in m_objGrid.SelectedCells)
                {
                    if ((m_SourceTable != null) && m_SourceTable.Columns.Contains(mCell.OwningColumn.DataPropertyName) && (!m_SourceTable.Columns[mCell.OwningColumn.DataPropertyName].AllowDBNull || !Globals.IsDBNull(m_SourceTable.Columns[mCell.OwningColumn.DataPropertyName].DefaultValue)))
                        continue;
                    //
                    if (!mCell.ReadOnly)
                    {
                        mCell.Value = DBNull.Value;
                        RaiseEvent_CellEdit(mCell);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex, "DataGridHandle.PreviewKeyDown");
        }
    }

    //Private Sub m_objGrid_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles m_objGrid.PreviewKeyDown

    //End Sub

    private void PasteFromClipboard()
    {
        string sTemp = Clipboard.GetText();
        if (string.IsNullOrEmpty(sTemp) || m_objGrid.ReadOnly)
        {
            return;
        }
        PasteTextData(sTemp, true);
    }

    private void PasteTextData4Cell(string[] arrRows)
    {
        if (arrRows.Length != m_objGrid.SelectedCells.Count)
        {
            //Return
        }
        DataGridViewCell mCell = default(DataGridViewCell);
        //
        bool bReverse = false;
        int iLastIndex = 0;
        int iIndex = 0;
        iLastIndex = m_objGrid.SelectedCells.Count - 1;
        //
        bReverse = (m_objGrid.SelectedCells[0].RowIndex > m_objGrid.SelectedCells[iLastIndex].RowIndex);
        //
        int iRowIndex = 0;
        int iCellIndex = 0;
        //Support paste continuous from one selected cell
        if (iLastIndex == 0)
        {
            iRowIndex = m_objGrid.SelectedCells[0].RowIndex;
            iCellIndex = m_objGrid.SelectedCells[0].ColumnIndex;
        }
        //
        for (int i = 0; i <= arrRows.Length - 1; i++)
        {
            if (i >= m_objGrid.SelectedCells.Count && iLastIndex > 0)
                return;
            if (bReverse)
            {
                iIndex = iLastIndex - i;
            }
            else
            {
                iIndex = i;
            }
            if (iLastIndex > 0)
            {
                mCell = m_objGrid.SelectedCells[iIndex];
            }
            else
            {
                if (iRowIndex + i == m_objGrid.Rows.Count)
                    return;
                //exit 
                mCell = m_objGrid.Rows[iRowIndex + i].Cells[iCellIndex];
            }
            if (mCell.ReadOnly)
                continue;
            if (mCell.OwningRow.IsNewRow)
            {
                m_objGrid.CurrentCell = mCell;
                m_objGrid.BeginEdit(false);
                m_objGrid.EditingControl.Text = Globals.Object_GetValue<string>( Globals.Object_SetValueEx(arrRows[i], mCell.ValueType.Name, true));
                m_objGrid.EndEdit();
            }
            else
            {
                mCell.Value = Globals.Object_SetValueEx(arrRows[i], mCell.ValueType.Name, true);
            }
            //
            RaiseEvent_CellEdit(mCell);
            //RaiseEvent After_PasteCellData(mCell.RowIndex, mCell.ColumnIndex)
        }
    }


    public void PasteTextData(string sTextData, bool bFromClipboard = false)
	{
		_PasteClipboard = bFromClipboard;
		sTextData = sTextData.Replace(Globals.vbCrLf, Globals.vbLf);
		string[] arrRows = sTextData.Split(new string[]{Globals.vbLf},StringSplitOptions.None);
		//------------------------------------------
		//Check wrong format
		//Data copy must include column name
		//------------------------------------------
		string[] arrHeader = arrRows[0].Split(new string[]{Globals.vbTab},StringSplitOptions.None);
		if (arrHeader.Length == 1) {
			PasteTextData4Cell(arrRows);
			return;
		}
		if (bFromClipboard && !m_objGrid.CurrentRow.IsNewRow)
			return;
		if (arrRows.Length == 1)
			return;
		//Replace Column Mapping
		if ((m_dicMappingColumn != null) && m_dicMappingColumn.Count > 0) {
			for (int i = 0; i <= arrHeader.Length - 1; i++) {
				if (m_dicMappingColumn.ContainsKey(arrHeader[i])) {
					arrHeader[i] = m_dicMappingColumn[arrHeader[i]];
				}
			}
		}
		//
		DataTable mDataSource = default(DataTable);
		if (m_objGrid.DataSource is DataTable) {
			mDataSource = (DataTable)m_objGrid.DataSource;
		} else {
			mDataSource = ((DataView)m_objGrid.DataSource).Table;
		}
		DataTable mDataTemp = mDataSource.Copy();
		mDataTemp.AcceptChanges();
		//
		DataRow mRow = default(DataRow);
		string sFieldName = null;
		//
		for (int i = 1; i <= arrRows.Length - 1; i++) {
			string[] arrFields = arrRows[i].Split(new string[]{ Globals.vbTab},StringSplitOptions.None);
			mRow = mDataTemp.NewRow();
			//Continue next row when row field length <> column length
			if (arrFields.Length != arrHeader.Length)
				continue;
			//Handle data error
			try {
				for (int j = 0; j <= arrFields.Length - 1; j++) {
					sFieldName = arrHeader[j];
					if (!mDataTemp.Columns.Contains(sFieldName))
						continue;

					if (sFieldName.ToUpper() == _sPrimaryKey.ToUpper()) {
						mRow[sFieldName] = DBNull.Value;
					} else if (m_objGrid.Columns.Contains(sFieldName) && m_objGrid.Columns[sFieldName].ReadOnly && !Globals.IsDBNull(mDataSource.Columns[sFieldName].DefaultValue)) {
						mRow[sFieldName] = mDataSource.Columns[sFieldName].DefaultValue;
					} else {
						if (arrFields[j].Length == 0) {
							if (mDataSource.Columns[sFieldName].AllowDBNull || mDataSource.Columns[sFieldName].DefaultValue == null) {
								mRow[sFieldName] = DBNull.Value;
							} else {
								mRow[sFieldName] = mDataSource.Columns[sFieldName].DefaultValue;
							}
						} else {
							if ((m_dicMappingValue != null) && m_dicMappingValue.ContainsKey(arrFields[j])) {
								arrFields[j] = m_dicMappingValue[arrFields[j]];
							}
							mRow[sFieldName] = arrFields[j];
						}
					}
				}
				mDataTemp.Rows.Add(mRow);
				bool bPassed = true;
				if (Row_PasteVerify != null) {
					Row_PasteVerify(mRow, ref bPassed);
				}
				if (!bPassed) {
					mDataTemp.Rows.Remove(mRow);
				}
			} catch {
				FormGlobals.App_ProgressPause();
				DialogResult rs = FormGlobals.Message_YesNoCancel("Some error occur when paste rows data." + Globals.vbCr + "Click Yes to continue with others rows." + Globals.vbCr + "Click No to stop with current rows." + Globals.vbCr + "Click Cancel to cancel paste data.");
				if (rs == DialogResult.Yes) {
					FormGlobals.App_ProgressResume();
					continue;
				} else if (rs == DialogResult.No) {
					break; // TODO: might not be correct. Was : Exit For
				} else {
					return;
				}
			}
		}
		//Finish add rows
		int iRowCount = 0;
		mDataTemp = mDataTemp.GetChanges(DataRowState.Added);
		if ((mDataTemp != null))
			iRowCount = mDataTemp.Rows.Count;
		if (iRowCount > 0) {
			int iRowPos = 0;
			//
			string sOldFilter = mDataSource.DefaultView.RowFilter;
			mDataSource.DefaultView.RowFilter = "";
			//
			int rowIndex = 0;
			foreach (DataRow mRow2 in mDataTemp.Rows) {
				iRowPos += 1;
				if (Row_PasteProgress != null) {
					Row_PasteProgress(iRowPos, iRowCount);
				}
				mDataSource.ImportRow(mRow2);
				if (bFromClipboard) {
					rowIndex = m_objGrid.NewRowIndex - 2;
				} else {
					rowIndex = m_objGrid.NewRowIndex - 1;
				}
				foreach (DataGridViewCell mCell in m_objGrid.Rows[rowIndex].Cells) {
					if (!(mCell is DataGridViewButtonCell))
						if (Cell_Validated != null) {
							Cell_Validated(mCell);
						}
					if (!_isLookupInvoke && m_dicLookupInvoker.ContainsKey(m_objGrid.Columns[mCell.ColumnIndex].Name)) {
						Cell_Lookup(m_objGrid.Rows[mCell.RowIndex].Cells[mCell.ColumnIndex], LookupMode.CheckData);
					}
				}
				if (Row_Validated != null) {
					Row_Validated(m_objGrid.Rows[rowIndex]);
				}
			}
			//
			if (iRowCount > 0 && bFromClipboard) {
				_deleteIndex = m_objGrid.CurrentRow.Index;
				m_objGrid.Rows.Remove(m_objGrid.CurrentRow);
			}
			//
			mDataSource.DefaultView.RowFilter = sOldFilter;
		}
		//
		if ((mDataTemp != null))
			mDataTemp.Dispose();
	}


    private int _deleteIndex = -1;
    private void CopyToClipboard()
    {
        string sTemp = null;

        if (m_objGrid.SelectedRows.Count == 0)
        {
            if (m_objGrid.SelectedCells.Count == 0)
                return;
            //For Step=-1 : support right order when copy select multi rows
            //For i As Integer = m_objGrid.SelectedCells.Count - 1 To 0 Step -1
            bool bReverse = false;
            int iLastIndex = 0;
            int iIndex = 0;
            iLastIndex = m_objGrid.SelectedCells.Count - 1;
            //
            bReverse = (m_objGrid.SelectedCells[0].RowIndex > m_objGrid.SelectedCells[iLastIndex].RowIndex);
            //
            for (int i = 0; i <= iLastIndex; i++)
            {
                if (bReverse)
                {
                    iIndex = iLastIndex - i;
                }
                else
                {
                    iIndex = i;
                }
                if (i == 0)
                {
                    sTemp = Globals.DB_GetString(m_objGrid.SelectedCells[iIndex].Value);
                }
                else
                {
                    sTemp += Globals.vbCrLf + Globals.DB_GetString(m_objGrid.SelectedCells[iIndex].Value);
                }
            }
            Clipboard.SetText(sTemp);
            return;
        }
        //Disable copy
        if (m_objGrid.ClipboardCopyMode != DataGridViewClipboardCopyMode.Disable)
            m_objGrid.ClipboardCopyMode = DataGridViewClipboardCopyMode.Disable;
        DataTable mDataSource = default(DataTable);
        if (m_objGrid.DataSource is DataTable)
        {
            mDataSource = (DataTable)m_objGrid.DataSource;
        }
        else
        {
            mDataSource = ((DataView)m_objGrid.DataSource).Table;
        }


        DataRowView dtRow = default(DataRowView);
        string sColumn = null;
        string sRow = null;
        //------------------------------------------------
        // Don't Copy column which is
        // - Primary Column
        // - Not contains in Grid column and Column Name <> Column Caption
        // - Grid Column is not visible
        //------------------------------------------------
        foreach (DataColumn mCol in mDataSource.Columns)
        {
            if (mCol.ColumnName == _sPrimaryKey || (!m_objGrid.Columns.Contains(mCol.ColumnName) && mCol.ColumnName == mCol.Caption) || (m_objGrid.Columns.Contains(mCol.ColumnName) && !m_objGrid.Columns[mCol.ColumnName].Visible))
            {
                mCol.AllowDBNull = true;
                continue;
            }
            if (!string.IsNullOrEmpty(sRow))
            {
                sRow += Globals.vbTab;
            }
            sRow += mCol.ColumnName;
        }
        string[] arrColumns = sRow.Split(new string[] { Globals.vbTab }, StringSplitOptions.None);
        sTemp = sRow;

        //For Step=-1 : support right order when copy select multi rows
        for (int i = m_objGrid.SelectedRows.Count - 1; i >= 0; i += -1)
        {
            DataGridViewRow mRow = m_objGrid.SelectedRows[i];
            if (!(mRow.IsNewRow || mRow.DataBoundItem == null))
            {
                dtRow = (DataRowView)mRow.DataBoundItem;
                sRow = null;
                foreach (string sColumn_loopVariable in arrColumns)
                {
                    sColumn = sColumn_loopVariable;
                    if ((sRow != null))
                    {
                        sRow += Globals.vbTab;
                    }
                    sRow += Globals.DB_GetString(dtRow[sColumn]);
                }
            }
            else
            {
                continue;
            }
            sTemp += Globals.vbCrLf + sRow;
        }
        Clipboard.SetText(sTemp);
    }

    private void m_objGrid_ColumnAdded(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
    {
        _mergeCalculate = true;
    }

    private void m_objGrid_ColumnRemoved(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
    {
        _mergeCalculate = true;
    }

    private void m_objGrid_ColumnStateChanged(object sender, System.Windows.Forms.DataGridViewColumnStateChangedEventArgs e)
    {
        if (e.StateChanged == DataGridViewElementStates.Visible)
        {
            _mergeCalculate = true;
            //
            if (m_objGrid.Columns.Contains(e.Column.Name + "_clmLookup"))
            {
                m_objGrid.Columns[e.Column.Name + "_clmLookup"].Visible = e.Column.Visible;
            }
            else if (m_objGrid.Columns.Contains(e.Column.Name + "_clmCalendar"))
            {
                m_objGrid.Columns[e.Column.Name + "_clmCalendar"].Visible = e.Column.Visible;
            }
        }
    }

    private bool _mergeCalculate;
    private bool _mergeRePaint;
    private string _mergeTheme;
    private List<int> _mergeColumn;
    private void Merge_Calculate()
    {
        if (!_mergeCalculate)
            return;
        _mergeColumn = new List<int>();
        _mergeTheme = FormGlobals.App_GetTheme();
        foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo)
        {
            objInfo.FromIndex = m_objGrid.Columns[objInfo.FromName].Index;
            objInfo.ToIndex = objInfo.FromIndex + objInfo.ColSpan - 1;
            //
            objInfo.Width = -3;
            for (int i = objInfo.FromIndex; i <= objInfo.ToIndex; i++)
            {
                objInfo.Width += m_objGrid.Columns[i].Width;
                _mergeColumn.Add(i);
            }
        }
        _mergeCalculate = false;
    }

    private void m_objGrid_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {
        if (m_dicMergeInfo == null)
            return;
        if (!_mergeRePaint)
        {
            _mergeRePaint = true;
            Merge_Calculate();
            if (m_dicMergeInfo.Count > 0)
            {
                foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo)
                {
                    PaintMergeHeader(objInfo);
                }
                //
                int colIndex = 0;
                for (colIndex = 0; colIndex <= m_objGrid.Columns.Count - 1; colIndex++)
                {
                    if (!m_objGrid.Columns[colIndex].Frozen)
                    {
                        break; // TODO: might not be correct. Was : Exit For
                    }
                    else
                    {
                        if (!_mergeColumn.Contains(colIndex))
                            m_objGrid.InvalidateCell(colIndex, -1);
                    }
                }
                m_objGrid.Invalidate(new Rectangle(0, 0, m_objGrid.RowHeadersWidth, m_objGrid.ColumnHeadersHeight));
            }
        }
        else
        {
            _mergeRePaint = false;
        }
    }

    private MergeColumnHeaderInfo GetMergeInfo(int columnIndex)
    {
        foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo)
        {
            if (objInfo.FromIndex <= columnIndex && objInfo.ToIndex >= columnIndex)
            {
                return objInfo;
            }
        }
        return null;
    }

    private void m_objGrid_CellPainting(object sender, System.Windows.Forms.DataGridViewCellPaintingEventArgs e)
    {
        if (e.RowIndex == -1 && e.ColumnIndex > -1)
        {
            Merge_Calculate();
            MergeColumnHeaderInfo objInfo = GetMergeInfo(e.ColumnIndex);
            //
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;
            string sCaption = m_objGrid.Columns[e.ColumnIndex].HeaderText;
            //
            if (objInfo == null)
            {
                e.PaintBackground(e.CellBounds, true);
                e.Paint(e.CellBounds, DataGridViewPaintParts.ContentBackground);
                e.Graphics.DrawString(m_objGrid.Columns[e.ColumnIndex].HeaderText, FormGlobals.Control_StyleFont(e.CellStyle.Font, FormGlobals.CS_GRID_HEADER), new SolidBrush(FormGlobals.Control_StyleColor(e.CellStyle.ForeColor, FormGlobals.CS_GRID_HEADER)), e.CellBounds, format);
                e.Handled = true;
            }
            else
            {
                if (!objInfo.Cover)
                {
                    Rectangle r2 = e.CellBounds;
                    r2.Height = (r2.Height / 2) - 2;
                    r2.Y = r2.Height + 5;
                    //
                    e.PaintBackground(r2, true);
                    e.Graphics.DrawString(sCaption, m_objGrid.ColumnHeadersDefaultCellStyle.Font, new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.ForeColor), r2, format);
                    e.Handled = true;
                }
            }
        }
        else
        {
            if (e.ColumnIndex >= 0 && e.RowIndex >= 0)
            {
                if (m_objGrid.Columns[e.ColumnIndex].Name.EndsWith("_clmCalendar"))
                {
                    e.Paint(e.CellBounds, DataGridViewPaintParts.All);

                    e.Graphics.DrawImage(Resources.btnCalendar, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
                    e.Handled = true;
                }
                else if (m_objGrid.Columns[e.ColumnIndex].Name.EndsWith("_clmLookup"))
                {
                    e.Paint(e.CellBounds, DataGridViewPaintParts.All);
                    
                    e.Graphics.DrawImage(Resources.btnFind, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
                    e.Handled = true;
                }
                else if (m_objGrid.Columns[e.ColumnIndex].Name == cs_clmDeleteRow)
                {
                    e.Paint(e.CellBounds, DataGridViewPaintParts.All);
                    e.Graphics.DrawImage(Resources.btnDelete, e.CellBounds.Left + 2, e.CellBounds.Top + 2, 16, 16);
                    e.Handled = true;
                }
            }
        }

        ////Hien thi icon loi
        //if (!String.IsNullOrEmpty(e.ErrorText))
        //{
        //    //e.Graphics.FillRectangle(Brushes.Yellow, new Rectangle(e.CellBounds.Width - 10, 0, 10, 10));
        //    //e.CellStyle.BackColor = Color.Yellow;
        //    if (e.CellStyle.Alignment == DataGridViewContentAlignment.BottomRight
        //        || e.CellStyle.Alignment == DataGridViewContentAlignment.MiddleRight
        //        || e.CellStyle.Alignment == DataGridViewContentAlignment.BottomRight)
        //    {
        //        e.CellStyle.Padding = new Padding(e.CellStyle.Padding.Left, e.CellStyle.Padding.Top, e.CellStyle.Padding.Right + 20, e.CellStyle.Padding.Bottom);
        //    }
        //}
        
    }

    private void m_objGrid_ColumnWidthChanged(object sender, System.Windows.Forms.DataGridViewColumnEventArgs e)
    {
        try
        {
            if (!m_objGrid.Visible || m_dicMergeInfo == null)
                return;
            foreach (MergeColumnHeaderInfo objInfo in m_dicMergeInfo)
            {
                if (e.Column.DisplayIndex < objInfo.FromIndex || e.Column.DisplayIndex > objInfo.ToIndex)
                    continue;
                objInfo.Width = GetColWidth(objInfo.FromIndex, objInfo.ToIndex);
                PaintMergeHeader(objInfo);
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private int GetColWidth(int iFromColIndex, int iToColIndex)
    {
        int iW = 0;
        for (int i = iFromColIndex; i <= iToColIndex; i++)
        {
            iW += m_objGrid.Columns[i].Width;
        }
        return iW;
    }

    private void PaintMergeHeader(MergeColumnHeaderInfo objInfo)
    {
        Rectangle recHeader = new Rectangle();
        int startIndex = -1;
        for (int colIndex = objInfo.FromIndex; colIndex <= objInfo.ToIndex; colIndex++)
        {
            Rectangle cellRec = m_objGrid.GetCellDisplayRectangle(colIndex, -1, true);
            if (cellRec.Width > 0)
            {
                if (startIndex == -1)
                {
                    startIndex = colIndex;
                    recHeader.X += cellRec.X;
                    recHeader.Height = cellRec.Height;
                }
            }
            if (colIndex <= startIndex || startIndex == -1)
            {
                if (colIndex < m_objGrid.Columns.Count - 1 && !m_objGrid.GetCellDisplayRectangle(colIndex + 1, -1, true).IsEmpty)
                {
                    recHeader.X -= m_objGrid.Columns[colIndex].Width - cellRec.Width;
                }
            }
        }
        if (startIndex == -1)
            return;
        //
        int w2 = objInfo.Width;
        //
        recHeader.X += 1;
        recHeader.Y += 2;
        recHeader.Width = w2;

        if (_mergeTheme == "WindowsClassic")
        {
            recHeader.Y += 1;
            if (objInfo.Cover)
                recHeader.Height -= 3;
        }

        Graphics objGraphics = default(Graphics);
        StringFormat format = new StringFormat();
        format.Alignment = (StringAlignment)GetStringAlignment(m_objGrid.Columns[objInfo.FromIndex].HeaderCell.Style.Alignment);
        format.LineAlignment = StringAlignment.Center;
        //
        objGraphics = m_objGrid.CreateGraphics();
        if (!objInfo.Cover)
            recHeader.Height = recHeader.Height / 2;
        //Draw background
        if (_mergeTheme == "WindowsClassic")
        {
            objGraphics.FillRectangle(new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.BackColor), recHeader);
        }
        else
        {
            Rectangle rTop = default(Rectangle);
            Rectangle rBottom = default(Rectangle);
            rTop = new Rectangle(recHeader.X, recHeader.Y, recHeader.Width, 0);
            rBottom = rTop;
            //
            rTop.Height = 10;
            rTop.Y += 1;
            //
            rBottom.Y = rTop.Bottom - 1;
            rBottom.Height = recHeader.Height - rTop.Height - 4;
            SolidBrush gbrush = new SolidBrush(FormGlobals.App_GetColor("#ffffff"));
            objGraphics.FillRectangle(gbrush, rTop);
            SolidBrush brushMiddle = new SolidBrush(FormGlobals.App_GetColor("#f7f8fa"));
            objGraphics.FillRectangle(brushMiddle, rBottom);
        }
        //Draw text
        objGraphics.DrawString(objInfo.Caption, m_objGrid.ColumnHeadersDefaultCellStyle.Font, new SolidBrush(m_objGrid.ColumnHeadersDefaultCellStyle.ForeColor), recHeader, format);
        //Draw line if not cover
        if (!objInfo.Cover)
        {
            Pen pen1 = default(Pen);
            Pen pen2 = default(Pen);
            if (_mergeTheme == "WindowsClassic")
            {
                pen1 = Pens.Gray;
                pen2 = Pens.WhiteSmoke;
            }
            else
            {
                pen1 = Pens.LightGray;
                pen2 = Pens.White;
            }
            int h = recHeader.Height;
            objGraphics.DrawLine(pen1, recHeader.Left, h - 0, recHeader.Left + w2 - 1, h - 0);
            objGraphics.DrawLine(pen2, recHeader.Left, h + 1, recHeader.Left + w2 - 1, h + 1);
        }
        objGraphics.Dispose();
    }

    private object GetStringAlignment(DataGridViewContentAlignment contentAlign)
    {
        switch (contentAlign)
        {
            case DataGridViewContentAlignment.MiddleRight:
            case DataGridViewContentAlignment.BottomRight:
            case DataGridViewContentAlignment.TopRight:
                return StringAlignment.Far;
            case DataGridViewContentAlignment.MiddleCenter:
            case DataGridViewContentAlignment.BottomCenter:
            case DataGridViewContentAlignment.TopCenter:
                return StringAlignment.Center;
            case DataGridViewContentAlignment.NotSet:
                return StringAlignment.Center;
            default:
                return StringAlignment.Near;
        }
    }

    private void m_objGrid_RowHeaderMouseClick(object sender, System.Windows.Forms.DataGridViewCellMouseEventArgs e)
    {
        try
        {
            m_objGrid.SelectionMode = DataGridViewSelectionMode.RowHeaderSelect;
            m_objGrid.Rows[e.RowIndex].Selected = true;
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_RowsAdded(object sender, System.Windows.Forms.DataGridViewRowsAddedEventArgs e)
    {
        try
        {
            if (_AutoRequired || m_dicRequiredColumn == null || m_SourceTable == null)
                return;
            //Correct default value for required columns
            string mColumnName = null;
            foreach (string mColumnName_loopVariable in m_dicRequiredColumn)
            {
                mColumnName = mColumnName_loopVariable;
                if (((m_objGrid.Rows[e.RowIndex].DataBoundItem != null)) 
                    && m_SourceTable.Columns.Contains(mColumnName) 
                    && ((m_SourceTable.Columns[mColumnName].DefaultValue != null)) 
                    && !Globals.IsDBNull(m_SourceTable.Columns[mColumnName].DefaultValue) 
                    //&& Globals.IsDBNull(m_objGrid.Rows[e.RowIndex].DataBoundItem[mColumnName]))
                    && Globals.IsDBNull(((DataRowView)m_objGrid.Rows[e.RowIndex].DataBoundItem).Row[mColumnName]))
                {
                    //m_objGrid.Rows[e.RowIndex].DataBoundItem(mColumnName) = m_SourceTable.Columns[mColumnName].DefaultValue;
                    ((DataRowView)m_objGrid.Rows[e.RowIndex].DataBoundItem).Row[mColumnName] = m_SourceTable.Columns[mColumnName].DefaultValue;
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_RowValidated(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        if ((m_objGrid.CurrentRow != null))
        {
            if (e.RowIndex == _deleteIndex)
            {
                _deleteIndex = -1;
                return;
            }
            m_objGrid.CurrentRow.Tag = null;
            if ((m_Form != null))
                m_Form.HandleEsc = false;
        }
    }

    private void m_objGrid_RowValidating(object sender, System.Windows.Forms.DataGridViewCellCancelEventArgs e)
    {
        try
        {
            if (m_objGrid.CurrentRow.Tag == null || m_bIsLoading)
                return;
            if (!m_objGrid.Rows[e.RowIndex].IsNewRow && (m_SourceTable != null) && e.RowIndex >= m_SourceTable.DefaultView.Count)
            {
                return;
            }
            ValidateRow(e.RowIndex);
            //Prevent unexpected row validated by raise on validating
            if (Row_Validated != null)
            {
                Row_Validated(m_objGrid.Rows[e.RowIndex]);
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void ValidateRow(int iRowIndex)
    {
        m_objGrid.Rows[iRowIndex].ErrorText = "";
        if (m_objGrid.Rows[iRowIndex].IsNewRow || m_SourceTable == null || iRowIndex >= m_SourceTable.DefaultView.Count)
            return;
        DataRow dr = default(DataRow);
        if (iRowIndex < m_SourceTable.DefaultView.Count && m_objGrid.Rows[iRowIndex].DataBoundItem is DataRowView)
        {
            dr = ((DataRowView)m_objGrid.Rows[iRowIndex].DataBoundItem).Row;
        }
        if ((dr != null))
            dr.RowError = "";

        string sDataColName = null;
        foreach (DataGridViewCell mCell in m_objGrid.Rows[iRowIndex].Cells)
        {
            sDataColName = m_objGrid.Columns[mCell.ColumnIndex].DataPropertyName;
            if (mCell is DataGridViewCheckBoxCell)
            {
                if (Globals.IsDBNull(mCell.Value))
                    mCell.Value = "0";
                continue;
            }
            else if (mCell is DataGridViewComboBoxCell)
            {
                if (!Globals.IsDBNull(mCell.Value) && mCell.Value.ToString() == mCell.EditedFormattedValue.ToString())
                {
                    if ((dr != null) && !string.IsNullOrEmpty(sDataColName))
                    {
                        dr.SetColumnError(sDataColName, "");
                    }
                    else
                    {
                        mCell.ErrorText = "";
                    }
                    continue;
                }
            }
            if ((m_dicRequiredColumn != null) && m_dicRequiredColumn.Contains(mCell.OwningColumn.DataPropertyName.ToUpper()))
            {
                if (string.IsNullOrEmpty(Globals.DB_GetString(mCell.Value)))
                {
                    if (string.IsNullOrEmpty(mCell.ErrorText))
                    {
                        mCell.ErrorText = "Required value";
                        if ((dr != null) && !string.IsNullOrEmpty(sDataColName))
                        {
                            dr.SetColumnError(sDataColName, "Required value");
                        }
                        else
                        {
                            mCell.ErrorText = "Required value";
                        }
                    }
                }
                else
                {
                    if (mCell.ErrorText == "Required value")
                    {
                        mCell.ErrorText = "";
                        if ((dr != null) && !string.IsNullOrEmpty(sDataColName))
                        {
                            dr.SetColumnError(sDataColName, "");
                        }
                        else
                        {
                            mCell.ErrorText = "";
                        }
                        continue;
                    }
                }
            }
            if (!string.IsNullOrEmpty(mCell.ErrorText))
            {
                string sHead = mCell.OwningColumn.HeaderText;
                if ((mCell.OwningColumn.Tag != null))
                    sHead = mCell.OwningColumn.Tag.ToString();
                if (string.IsNullOrEmpty(sHead))
                    sHead = mCell.OwningColumn.ToolTipText;
                if (string.IsNullOrEmpty(m_objGrid.Rows[iRowIndex].ErrorText))
                {
                    if ((dr != null))
                    {
                        dr.RowError += "Error in Cell(s): '" + sHead + "'";
                    }
                    else
                    {
                        m_objGrid.Rows[iRowIndex].ErrorText += "Error in Cell(s): '" + sHead + "'";
                    }
                }
                else
                {
                    if ((dr != null))
                    {
                        dr.RowError += ", '" + sHead + "'";
                    }
                    else
                    {
                        m_objGrid.Rows[iRowIndex].ErrorText += ", '" + sHead + "'";
                    }
                }
            }
        }
    }

    private void m_objGrid_UserDeletingRow(object sender, System.Windows.Forms.DataGridViewRowCancelEventArgs e)
    {
        int iCount = m_objGrid.SelectedRows.Count;
        if (m_objGrid.NewRowIndex >= 0 && m_objGrid.SelectedRows.Contains(m_objGrid.Rows[m_objGrid.NewRowIndex]))
        {
            iCount -= 1;
        }
        if (!e.Cancel)
        {
            if (iCount > 1 || _MultiDelete)
            {
                if (!_MultiDelete)
                {
                    _MultiDelete = true;
                    _ConfirmDelete = FormGlobals.Message_Delete("current selected rows");
                    _RowSelectCount = iCount;
                }
                e.Cancel = !_ConfirmDelete;
                _RowSelectCount -= 1;
                if (_RowSelectCount == 0)
                {
                    _MultiDelete = false;
                }
            }
            else
            {
                e.Cancel = !FormGlobals.Message_Delete("current row");
            }
        }
        if ((!e.Cancel))
        {
            if (Row_Deleting != null)
            {
                bool cancel = false;
                Row_Deleting(e.Row, ref cancel );
                e.Cancel = cancel;
            }
        }
    }

    private void m_txtCellEdit_Leave(object sender, System.EventArgs e)
    {
        try
        {
            m_txtCellEdit.Visible = false;
            m_objGrid.Enabled = true;
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    //Private Sub m_txtCellEdit_PreviewKeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles m_txtCellEdit.PreviewKeyDown
    private void m_txtCellEdit_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
    {
        try
        {
            if (e.KeyCode == Keys.Enter)
            {
                string sText = m_txtCellEdit.Text;
                if (m_txtCellEdit.Tag == "String")
                {
                    ProcessMultiCellEdit(m_txtCellEdit.Text);
                }
                else
                {
                    if (sText.Length == 0)
                    {
                        ProcessMultiCellEdit("");
                        return;
                    }
                    string sFormula = null;
                    decimal iValue = default(decimal);
                    if (Formula_Prepare(sText, ref sFormula, ref iValue))
                    {
                        ProcessMultiCellEdit(sFormula, iValue);
                    }
                    else
                    {
                        FormGlobals.Message_Information("Invalid formula or value for multi cell edit." + Globals.vbCr + "Formula support is  + - * / and r (round) or o (auto order)." + Globals.vbCr + "Please contact system administrator for more information.");
                    }
                }
            }
            else if (e.KeyCode == Keys.Escape)
            {
            }
            else if (e.KeyCode == Keys.Tab)
            {
            }
            else
            {
                return;
            }
            //
            //e.IsInputKey = False
            e.SuppressKeyPress = true;
            //
            m_txtCellEdit.Visible = false;
            m_objGrid.Enabled = true;
            m_objGrid.Focus();
            if ((m_Form != null))
                m_Form.HandleEsc = false;
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void ProcessMultiCellEdit(string sFormula, decimal iValue)
    {
        if (sFormula == "/" && iValue == 0)
            return;
        if (sFormula == "o")
            m_iOrder = 0;
        DataGridViewCell mCell = default(DataGridViewCell);
        //For Each mCell As DataGridViewCell In m_objGrid.SelectedCells
        for (int i = m_objGrid.SelectedCells.Count - 1; i >= 0; i += -1)
        {
            mCell = m_objGrid.SelectedCells[i];
            if (mCell.ReadOnly || mCell.OwningRow.IsNewRow)
                continue;
            mCell.Tag = "Edit";
            Formula_Calculate(mCell, sFormula, iValue);
            //
            RaiseEvent_CellEdit(mCell);
        }
    }

    private void RaiseEvent_CellEdit(DataGridViewCell mCell)
    {
        if (mCell.OwningColumn.HeaderCell.Tag == "DateShort")
        {
            ValidateDateShort(Globals.DB_GetString(mCell.Value), mCell.ColumnIndex, mCell.RowIndex);
        }
        m_objGrid.Rows[mCell.RowIndex].Tag = "Edit";
        //RaiseEvent_CellEdit(mCell)
        //RaiseEvent After_PasteCellData(mCell.RowIndex, mCell.ColumnIndex)
        if (Cell_Validated != null)
        {
            Cell_Validated(mCell);
        }
    }

    private void ProcessMultiCellEdit(string sValue)
    {
        foreach (DataGridViewCell mCell in m_objGrid.SelectedCells)
        {
            if (mCell.ReadOnly || mCell.OwningRow.IsNewRow)
                continue;
            mCell.Tag = "Edit";
            //            
            if (string.IsNullOrEmpty(sValue))
            {
                mCell.Value = DBNull.Value;
            }
            else
            {
                mCell.Value = sValue;
            }
            RaiseEvent_CellEdit(mCell);
        }
    }

    private void Formula_Calculate(DataGridViewCell mCell, string sFormula, decimal iValue)
    {
        decimal tmp;
        switch (sFormula)
        {
            case "+":
                mCell.Value = Globals.DB_GetValue(mCell.Value, 0) + iValue;
                break;
            case "-":
                mCell.Value = Globals.DB_GetValue(mCell.Value, 0) - iValue;
                break;
            case "/":
                mCell.Value = Globals.DB_GetValue(mCell.Value, 0) / iValue;
                break;
            case "*":
                mCell.Value = Globals.DB_GetValue(mCell.Value, 0) * iValue;
                break;
            case "r":
                //Round by 1,10,100,1000
                tmp = Globals.DB_GetValue(mCell.Value, 0);
                tmp = tmp / iValue;
                tmp = decimal.Round(tmp, 0);
                tmp = tmp * iValue;
                mCell.Value = tmp;
                break;
            case "rh":
                //Round to five. Ex: 0.1-0.4 -> 0.5; 0.6-0.9 -> 1.0
                tmp = Globals.DB_GetValue(mCell.Value, 0);
                tmp = tmp / (iValue / (decimal)0.5);
                tmp = decimal.Round(tmp, 0) * (iValue / (decimal)0.5);
                if (tmp < Globals.DB_GetValue(mCell.Value, 0))
                {
                    tmp += iValue;
                }
                mCell.Value = tmp;
                break;
            case "o":
                //Update order code by row position
                m_iOrder += 1;
                mCell.Value = iValue + m_iOrder;
                break;
            default:
                mCell.Value = iValue;
                break;
        }
    }

    private bool Formula_Prepare(string sText, ref string sFormula, ref decimal iValue)
    {
        if (sText.Length == 0)
            return false;
        string sValue = null;
        bool bHasPercent = false;
        sFormula = sText[0].ToString();
        sFormula = sFormula.ToLower();
        if (!"+-/*ro".Contains(sFormula))
        {
            sValue = sText;
            sFormula = "";
        }
        else
        {
            if (sText[sText.Length - 1].ToString() == "%")
            {
                bHasPercent = true;
                sValue = sText.Substring(1, sText.Length - 2);
            }
            else
            {
                sValue = sText.Substring(1);
            }
            if (sFormula == "o" && string.IsNullOrEmpty(sValue))
            {
                sValue = "0";
            }
        }
        //
        if (Globals.IsNumeric(sValue))
        {
            if (bHasPercent)
            {
                switch (sFormula)
                {
                    case "-":
                        sFormula = "*";
                        iValue = 1 - (decimal.Parse(sValue) / 100);
                        break;
                    case "+":
                        sFormula = "*";
                        iValue = 1 + (decimal.Parse(sValue) / 100);
                        break;
                    default:
                        iValue = (decimal.Parse(sValue) / 100);
                        break;
                }
            }
            else
            {
                if (!decimal.TryParse(sValue,out iValue))
                    return false;
                if (sFormula == "r")
                {
                    if ("5,50,500,5000,".Contains(sValue + ","))
                    {
                        //Round to 5
                        sFormula = "rh";
                    }
                    else if (!"1,10,100,1000,".Contains(sValue + ","))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        else
        {
            return false;
        }
    }

    private decimal Formula_Evaluate(string sExp)
    {
        try
        {
            System.Data.DataTable table = new System.Data.DataTable();
            table.Columns.Add("expression", string.Empty.GetType(), sExp);
            DataRow row = table.NewRow();
            table.Rows.Add(row);
            return decimal.Parse(row["expression"].ToString());
        }
        catch
        {
            return Null.NullDecimal;
        }
    }
    //
    private void ctxMenu_Click(object sender, System.EventArgs e)
    {
        switch (((ToolStripItem)sender).Name)
        {
            case "Filter_Edit":
                if (m_dicFilterColumn == null)
                    return;
                foreach (DataRow dr in m_SourceTable.GetErrors())
                {
                    if (dr.RowState != DataRowState.Deleted)
                    {
                        FormGlobals.Message_Information("Please correct all rows error before filter data");
                        return;
                    }
                }

                //
                frmGridFilter f = new frmGridFilter();
                DataGridViewRow mRow = default(DataGridViewRow);
                f.Column_Name.DisplayMember = "DisplayField";
                f.Column_Name.ValueMember = "ValueField";
                foreach (string sKey in m_dicFilterColumn.Keys)
                {
                    ListBoxItem itm = new ListBoxItem(sKey, m_dicFilterColumn[sKey]);
                    f.Column_Name.Items.Add(itm);
                }

                //
                DataTable dt = m_SourceTable.Clone();
                dt.DefaultView.RowFilter = m_SourceTable.DefaultView.RowFilter;
                if (f.Show_Form(dt))
                {
                    frmProgress.Instance().ThreadSub = Filter_Apply;
                    frmProgress.Instance().ThreadInputObject = f.Tag;
                    frmProgress.Instance().Show_Progress("Please wait ...");
                }
                f.Close();
                break;
            case "Filter_Clear":
                frmProgress.Instance().ThreadSub = Filter_Apply;
                frmProgress.Instance().ThreadInputObject = "";
                frmProgress.Instance().Show_Progress("Please wait ...");
                break;
        }
    }

    private void Filter_Apply(object sFilter)
    {
        if (sFilter == null)
            sFilter = "";
        if ((m_objGrid.CurrentRow != null) && m_objGrid.CurrentRow.IsNewRow)
        {
            m_objGrid.CancelEdit();
        }
        m_SourceTable.DefaultView.RowFilter = sFilter.ToString();
        if ((m_objGrid.CurrentRow != null))
        {
            m_objGrid.InvalidateRow(m_objGrid.CurrentRow.Index);
        }
        m_objGrid.Refresh();
        if (string.IsNullOrEmpty(m_SourceTable.DefaultView.RowFilter))
        {
            m_objGrid.TopLeftHeaderCell.Style.BackColor = Color.Transparent;
        }
        else
        {
            m_objGrid.TopLeftHeaderCell.Style.BackColor = Color.LightGoldenrodYellow;
        }
    }

    private void m_objGrid_ReadOnlyChanged(object sender, System.EventArgs e)
    {
        if (m_objGrid.ReadOnly)
        {
            m_objGrid.TopLeftHeaderCell.ContextMenuStrip = null;
        }
        else
        {
            m_objGrid.TopLeftHeaderCell.ContextMenuStrip = mnuContext;
        }
    }

    private void m_objGrid_CellErrorTextChanged(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {

            DataGridViewCell mCell = m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex];
            if (mCell.OwningRow.DataBoundItem is DataRowView)
            {
                if (!string.IsNullOrEmpty(mCell.OwningColumn.DataPropertyName))
                {
                    ((DataRowView)mCell.OwningRow.DataBoundItem).Row.SetColumnError(mCell.OwningColumn.DataPropertyName, mCell.ErrorText);
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_RowErrorTextChanged(object sender, System.Windows.Forms.DataGridViewRowEventArgs e)
    {
        if ((m_SourceTable != null) && e.Row.Index < m_SourceTable.DefaultView.Count)
        {
            ((DataRowView)e.Row.DataBoundItem).Row.RowError = e.Row.ErrorText;
        }
    }

    private void m_objGrid_EditingControlShowing(object sender, System.Windows.Forms.DataGridViewEditingControlShowingEventArgs e)
    {
        if (e.Control is ComboBox)
        {
            ((ComboBox)e.Control).DropDownStyle = ComboBoxStyle.DropDown;
            //DirectCast(e.Control, ComboBox).DroppedDown = True
        }
        else if (e.Control is TextBox)
        {
            if (m_objGrid.CurrentCell.ValueType.Name == "DateTime" && !Globals.IsDBNull(m_objGrid.CurrentCell.Value))
            {
                if( (m_objGrid.CurrentCell.OwningColumn.DefaultCellStyle.Format)
                
                    == Globals.CS_DISPLAY_DATE_FORMAT){
                        m_objGrid.EditingControl.Text = ((DateTime)m_objGrid.CurrentCell.Value).ToString(Globals.CS_EDIT_DATE_FORMAT);
                        //break;
                }
                else if   ( (m_objGrid.CurrentCell.OwningColumn.DefaultCellStyle.Format)
                    == Globals.CS_DISPLAY_TIME_FORMAT){
                        m_objGrid.EditingControl.Text = ((DateTime)m_objGrid.CurrentCell.Value).ToString(Globals.CS_EDIT_TIME_FORMAT);
                       // break;
                }
            }
            //
            if (m_dicCaseInput.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name))
            {
                ((TextBox)e.Control).CharacterCasing = m_dicCaseInput[m_objGrid.CurrentCell.OwningColumn.Name];
            }
            else
            {
                ((TextBox)e.Control).CharacterCasing = CharacterCasing.Normal;
            }
            //
            int iMaxLength = 0;
            if (m_SourceTable != null && m_objGrid.CurrentCell.OwningColumn.DataPropertyName != null && m_SourceTable.Columns.Contains(m_objGrid.CurrentCell.OwningColumn.DataPropertyName))
            {
                iMaxLength = m_SourceTable.Columns[m_objGrid.CurrentCell.OwningColumn.DataPropertyName].MaxLength;
            }
            else if (m_dicMaxInput != null && m_dicMaxInput.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name))
            {
                iMaxLength = m_dicMaxInput[m_objGrid.CurrentCell.OwningColumn.Name];
            }
            if (iMaxLength < 0)
                iMaxLength = 0;
            ((TextBox)e.Control).MaxLength = iMaxLength;
            //
            if (e.Control.Tag == null)
            {
                e.Control.Tag = "HANDLER";
                ((TextBox)e.Control).PreviewKeyDown += m_objGrid_PreviewKeyDown;
            }
        }
    }

    //Sự kiện giá trị của Cell đã thay đổi
    private void m_objGrid_CellValueChanged(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {
            if (e.RowIndex < 0 || e.ColumnIndex < 0)
                return;
            if (e.RowIndex > m_objGrid.RowCount - 1)
                return;
            if (!_isLookupInvoke)
                RaiseEvent_CellEdit(m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex]);
            //
            if (!_isLookupInvoke && m_dicLookupInvoker.ContainsKey(m_objGrid.Columns[e.ColumnIndex].Name))
            {
                Cell_Lookup(m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex], LookupMode.CheckData);
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_CellDoubleClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {
            if (e.RowIndex >= 0 && (m_DbClickMethod != null))
            {
                m_DbClickMethod.Invoke();
            }
            if (m_objGrid.ReadOnly)
                m_objGrid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private int _prevRowIndex = -1;
    private void m_objGrid_SelectionChanged(object sender, System.EventArgs e)
    {
        if (m_IsLoading)
            return;
        //
        if (m_objGrid.SelectedRows.Count > 0 && (m_objGrid.CurrentRow == null || m_objGrid.SelectedRows[0].Index != m_objGrid.CurrentRow.Index))
        {
            foreach (DataGridViewCell cell in m_objGrid.SelectedRows[0].Cells)
            {
                if (cell.Visible)
                {
                    m_objGrid.CurrentCell = cell;
                    break; // TODO: might not be correct. Was : Exit For
                }
            }
        }
        if ((m_objGrid.Visible & (m_objGrid.CurrentRow != null)) && m_objGrid.CurrentRow.Index != _prevRowIndex && (m_SelectMethod != null))
        {
            _prevRowIndex = m_objGrid.CurrentRow.Index;
            m_SelectMethod.Invoke();
        }
    }

    private void m_objGrid_CellFormatting(object sender, System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
    {
        try
        {
            if (e.ColumnIndex < 0 || e.RowIndex < 0)
                return;
            if (m_objGrid.Columns[e.ColumnIndex].ReadOnly && !m_objGrid.ReadOnly)
            {
                e.CellStyle.BackColor = FormGlobals.App_GetColor(FormGlobals.CS_STYLE_VIEW);
                //SystemColors.Control
                e.CellStyle.ForeColor = SystemColors.ControlText;
            }
            //
            if ((m_dicFormatColumn != null) && m_dicFormatColumn.ContainsKey(GetColumnKey(e.ColumnIndex).ToUpper()))
            {
                Dictionary<object, string> formatDic = m_dicFormatColumn[GetColumnKey(e.ColumnIndex).ToUpper()];
                if ((Globals.DB_GetValue(e.Value, null) != null) && formatDic.ContainsKey(e.Value))
                {
                    string[] arrColor = formatDic[e.Value].Split(new char[]{';'});
                    e.CellStyle.BackColor = FormGlobals.App_GetColor(arrColor[0]);
                    if (arrColor.Length > 1)
                        e.CellStyle.BackColor = FormGlobals.App_GetColor(arrColor[1]);
                }
            }
            //
            object oValue = Globals.DB_GetValue(e.Value);
            if (Cell_Format != null)
            {
                Cell_Format(m_objGrid.Columns[e.ColumnIndex].Name, e);
            }
            if (oValue != null) 
            e.FormattingApplied = oValue.GetType().Name != Globals.DB_GetValue(e.Value).GetType().Name || (oValue != Globals.DB_GetValue(e.Value));
            
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_CellContentClick(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {
            if (m_objGrid.Columns[e.ColumnIndex].Name.EndsWith("_clmCalendar"))
            {
                btnCalendar_Click(m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex - 1]);
            }
            else if (m_objGrid.Columns[e.ColumnIndex].Name.EndsWith("_clmLookup"))
            {
                string columnName = m_objGrid.Columns[e.ColumnIndex].Name;
                columnName = columnName.Substring(0, columnName.Length - "_clmLookup".Length);
                if (m_objGrid.Rows[e.RowIndex].Cells[columnName].ReadOnly == false)
                {
                    Cell_Lookup(m_objGrid.Rows[e.RowIndex].Cells[columnName], LookupMode.SelectData);
                }
            }
            else if (m_objGrid.Columns[e.ColumnIndex].Name == cs_clmDeleteRow)
            {
                //m_objGrid.Rows.RemoveAt(e.RowIndex)
                if (e.RowIndex != m_objGrid.NewRowIndex)
                {
                    m_objGrid.Rows[e.RowIndex].Selected = true;
                    SendKeys.SendWait("{DEL}");
                }
            }
            else if (m_objGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn)
            {
                if (CellButton_Click != null)
                {
                    CellButton_Click(m_objGrid.Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewButtonCell);
                }
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void btnCalendar_Click(DataGridViewCell cell)
    {
        Form objForm = m_objGrid.FindForm();
        MonthCalendar calendarPopup = objForm.Controls["calendarGrid"] as MonthCalendar;
        if (calendarPopup == null)
        {
            calendarPopup = new MonthCalendar();
            calendarPopup.Name = "calendarGrid";
            objForm.Controls.Add(calendarPopup);
            calendarPopup.Visible = false;
            calendarPopup.MaxSelectionCount = 1;
            calendarPopup.BackColor = Color.AliceBlue;
            //
            calendarPopup.DateSelected += Calendar_DateChanged;
            calendarPopup.Leave += Calendar_Leave;
        }
        //
        DateTime dValue = default(DateTime);
        if (Globals.Date_TryParseEx(cell.FormattedValue.ToString(),ref dValue))
        {
            calendarPopup.SetDate(dValue);
        }
        //
        Point locationOnForm = GetCellLocation(cell);
        //New Point(GetPosX(cell.ColumnIndex), GetPosY(cell.RowIndex))
        //
        calendarPopup.Location = new Point(locationOnForm.X + 1, locationOnForm.Y + cell.Size.Height + 1);
        if (calendarPopup.Left + calendarPopup.Width > objForm.Width - 6)
        {
            calendarPopup.Left = locationOnForm.X - (calendarPopup.Width - cell.Size.Width);
        }
        if (calendarPopup.Top + calendarPopup.Height > objForm.Height - 6)
        {
            calendarPopup.Top = locationOnForm.Y - (calendarPopup.Height);
        }
        calendarPopup.Visible = true;
        calendarPopup.BringToFront();
        calendarPopup.Focus();
        calendarPopup.Tag = cell;
    }

    private void Calendar_DateChanged(object sender, System.Windows.Forms.DateRangeEventArgs e)
    {
        DataGridViewCell cell = (sender as MonthCalendar).Tag as DataGridViewCell;
        cell.DataGridView.CurrentCell = cell;
        cell.DataGridView.BeginEdit(false);
        ((Control)sender).Visible = false;
        m_objGrid.EditingControl.Text = e.Start.ToString(Globals.CS_EDIT_DATE_FORMAT);
        m_objGrid.CurrentCell.Value = e.Start;
    }

    private void Calendar_Leave(object sender, System.EventArgs e)
    {
        ((Control)sender).Visible = false;
    }

    private void m_objGrid_CellEnter(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        try
        {
            if (m_dicLookupInit.ContainsKey(m_objGrid.Columns[e.ColumnIndex].Name))
            {
                if ((m_Form != null))
                    m_Form.HandleLookup = true;
            }
        }
        catch
        {
        }
    }

    private void m_objGrid_CellMouseLeave(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        if ((m_Form != null))
            m_Form.HandleLookup = false;
    }

    private void m_objGrid_PreviewKeyDown(object sender, System.Windows.Forms.PreviewKeyDownEventArgs e)
    {
        try
        {
            if (e.KeyData == FormGlobals.CS_LOOKUP_KEY && m_dicLookupInit.ContainsKey(m_objGrid.CurrentCell.OwningColumn.Name))
            {
                Cell_Lookup(m_objGrid.CurrentCell, LookupMode.SelectData);
            }
        }
        catch (Exception ex)
        {
            FormGlobals.Message_Error(ex);
        }
    }

    private void m_objGrid_RowEnter(object sender, System.Windows.Forms.DataGridViewCellEventArgs e)
    {
        //If Not m_SelectMethod Is Nothing Then
        //    m_SelectMethod.Invoke()
        //End If
    }

}

internal class MergeColumnHeaderInfo
{
    //Cấu trúc để chứa thông tin merge header
    //Tên cột bắt đầu
    public string FromName;
    //So cot merge
    public int ColSpan;
    //Index của cột bắt đầu (tự động tính)
    public int FromIndex;
    //Index của cột kết thúc (tự động tính)
    public int ToIndex;
    //Nhãn của merge header
    public string Caption;
    // Độ rộng (tự động tính)
    public int Width;
    // Yes: phủ toàn bộ các header; No: phủ 1/2 và giữ lại text của header
    public bool Cover;
}
